@vc-shell/vc-app-skill 2.5.0 → 2.6.0-pr370.de147ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/runtime/VERSION +1 -1
  3. package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
  4. package/runtime/knowledge/docs/core/blade-navigation/blade-nav-composables.docs.md +28 -1
  5. package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +1 -1
  6. package/runtime/knowledge/docs/core/composables/useKeyboardNavigation/useKeyboardNavigation.docs.md +8 -8
  7. package/runtime/knowledge/docs/core/composables/useModificationTracker/useModificationTracker.docs.md +11 -0
  8. package/runtime/knowledge/docs/core/composables/usePopup/usePopup.docs.md +2 -0
  9. package/runtime/knowledge/docs/core/composables/useSidebarState/useSidebarState.docs.md +15 -4
  10. package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +22 -18
  11. package/runtime/knowledge/docs/core/composables/useToolbar/useToolbar.docs.md +4 -0
  12. package/runtime/knowledge/docs/core/services/services.docs.md +0 -1
  13. package/runtime/knowledge/docs/ui/components/atoms/vc-button/vc-button.docs.md +1 -0
  14. package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +10 -9
  15. package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +21 -20
  16. package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +27 -26
  17. package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +1 -0
  18. package/runtime/knowledge/docs/ui/components/molecules/vc-popover/vc-popover.docs.md +3 -2
  19. package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +16 -11
  20. package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +1 -0
  21. package/runtime/knowledge/docs/ui/components/organisms/vc-app/vc-app.docs.md +1 -1
  22. package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +77 -0
  23. package/runtime/knowledge/docs/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.docs.md +20 -19
  24. package/runtime/knowledge/docs/ui/components/organisms/vc-scheduler/vc-scheduler.docs.md +4 -2
  25. package/runtime/vc-app.md +4 -2
  26. package/runtime/knowledge/docs/core/composables/useKeyboardShortcuts/useKeyboardShortcuts.docs.md +0 -219
  27. package/runtime/knowledge/docs/core/composables/useLatestRequest/useLatestRequest.docs.md +0 -149
  28. package/runtime/knowledge/docs/core/composables/useMenuExpanded/index.docs.md +0 -93
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vc-shell/vc-app-skill",
3
- "version": "2.5.0",
3
+ "version": "2.6.0-pr370.de147ea",
4
4
  "description": "AI coding skill for scaffolding and generating VirtoCommerce Shell applications. Works with Claude Code, OpenCode, Gemini, Codex, Cursor.",
5
5
  "bin": "./bin/install.cjs",
6
6
  "files": [
package/runtime/VERSION CHANGED
@@ -1 +1 @@
1
- 2.5.0
1
+ 2.6.0
@@ -1 +1 @@
1
- Synced from framework at commit 2964ccbe3 on 2026-08-19T10:23:33.758Z
1
+ Synced from framework at commit 1919c06af on 2026-09-22T14:57:56.467Z
@@ -44,10 +44,36 @@ The `createBladeStack` / `createBladeMessaging` factories are internal (not re-e
44
44
 
45
45
  The blade stack state machine. Manages an ordered array of `BladeDescriptor` objects.
46
46
 
47
- ### Factory: `createBladeStack(bladeRegistry, hasAccess?)`
47
+ ### Factory: `createBladeStack(bladeRegistry, hasAccess?, urlSink?)`
48
48
 
49
49
  Creates a new stack instance. Called once by the navigation plugin.
50
50
 
51
+ `urlSink` is where the stack writes the address bar. It defaults to a no-op, so a
52
+ stack created without one (Storybook, standalone mounts, unit tests) simply never
53
+ touches the URL. The plugin passes the router-backed sink.
54
+
55
+ ### The stack owns the URL
56
+
57
+ Every navigation action syncs the URL itself, with the verb the action implies.
58
+ Callers mutate the stack and nothing else — there is no "and now sync the URL"
59
+ step to forget.
60
+
61
+ | Action | Verb | When |
62
+ | --------------------- | --------- | -------------------------------------- |
63
+ | `openWorkspace` | `push` | the resulting active blade has a `url` |
64
+ | `openBlade` | `push` | the resulting active blade has a `url` |
65
+ | `coverCurrentBlade` | `push` | the resulting active blade has a `url` |
66
+ | `replaceCurrentBlade` | `replace` | the resulting active blade has a `url` |
67
+ | `closeBlade` | `replace` | the close was not prevented by a guard |
68
+ | `closeChildren` | `replace` | always |
69
+
70
+ Everything else — `updateBladeQuery`, `setBladeTitle`, `setBladeError`,
71
+ `_restoreStack` — never writes the URL.
72
+
73
+ The router guard restores the stack **from** the URL, so it suppresses the sink
74
+ for the duration of the navigation (`RouterUrlSink.suppressWhile`): during
75
+ resolution the URL is the source, and writing back would re-enter the guard.
76
+
51
77
  ### API
52
78
 
53
79
  | Method | Description |
@@ -162,6 +188,7 @@ The plain data object stored in the stack for each blade:
162
188
  - Close guards return `true` to PREVENT closing (opposite of the legacy convention where `false` prevented closing). The adapter handles the inversion.
163
189
  - `replaceCurrentBlade` destroys the current blade and creates a new one at the same index with the same `parentId`. Use `coverCurrentBlade` to hide instead of destroy — the covering blade's `callParent` reaches the hidden blade's exposed methods.
164
190
  - URL sync only updates the address bar for blades that have a `url` segment. Third-level detail panels without URLs leave the previous URL intact.
191
+ - The stack writes the URL, not the caller. `createUrlSync` remains for the two writes that are not stack mutations: the post-restore reconcile in `VcBladeNavigation` and the debounced table-query-state writer.
165
192
  - `useBladeNavigation()` requires the navigation plugin to be installed. It throws if called before plugin initialization.
166
193
 
167
194
  ## Related
@@ -790,7 +790,7 @@ if (param.value) {
790
790
  1. **Plugin setup:** `BladeNavigationPlugin` (plugin-v2) installs a `BladeStack` and `BladeMessaging` singleton and provides them via Vue's dependency injection.
791
791
  2. **Inside a blade:** `VcBladeSlot` provides a `BladeDescriptor` ref for each blade instance. `useBlade()` injects this descriptor to access identity, register guards, and set errors.
792
792
  3. **Outside a blade:** `useBlade()` falls back to the singleton instances. Only `openBlade` works because it does not need a blade descriptor.
793
- 4. **URL sync:** When blades with a `url` property are opened or closed, `useBlade()` synchronizes the browser URL via `createUrlSync`, enabling deep linking and back/forward navigation.
793
+ 4. **URL sync:** When blades with a `url` property are opened or closed, the blade stack synchronizes the browser URL itself (push on open, replace on close), enabling deep linking and back/forward navigation. `useBlade()` only mutates the stack.
794
794
 
795
795
  <!-- internal:end -->
796
796
 
@@ -71,13 +71,13 @@ function openMenu() {
71
71
 
72
72
  ### Parameters (Options Object)
73
73
 
74
- | Parameter | Type | Default | Description |
75
- | ------------------- | --------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- |
76
- | `containerSelector` | `string` | `'[role="menu"]'` | CSS selector to auto-find the container on mount. Set to empty string to disable auto-attach. |
77
- | `itemSelector` | `string` | `'[tabindex="0"]'` | CSS selector for focusable items inside the container. |
78
- | `onEnter` | `(el: HTMLElement) => void` | `undefined` | Called when Enter or Space is pressed on a focused item. Falls back to `el.click()` if not provided. |
79
- | `onEscape` | `() => void` | `undefined` | Called when Escape is pressed. Typically used to close the menu or popover. |
80
- | `loop` | `boolean` | `true` | When `true`, pressing ArrowDown on the last item wraps to the first, and ArrowUp on the first wraps to the last. |
74
+ | Parameter | Type | Default | Description |
75
+ | ------------------- | --------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------- |
76
+ | `containerSelector` | `string` | `'[role="menu"]'` | CSS selector to auto-find the container on mount. Set to empty string to disable auto-attach. |
77
+ | `itemSelector` | `string` | `'[tabindex="0"]'` | CSS selector for focusable items inside the container. |
78
+ | `onEnter` | `(el: HTMLElement) => void` | `undefined` | Called when Enter or Space is pressed on a focused item. Falls back to `el.click()` if not provided. |
79
+ | `onEscape` | `() => void` | `undefined` | Called when Escape is pressed, and makes the composable consume the key. Typically used to close the menu or popover. |
80
+ | `loop` | `boolean` | `true` | When `true`, pressing ArrowDown on the last item wraps to the first, and ArrowUp on the first wraps to the last. |
81
81
 
82
82
  ### Returns
83
83
 
@@ -101,7 +101,7 @@ The composable listens for `keydown` events on the container element and handles
101
101
  - **ArrowDown / Tab**: Move focus to the next item (`focusNextElement`)
102
102
  - **ArrowUp / Shift+Tab**: Move focus to the previous item (`focusPreviousElement`)
103
103
  - **Enter / Space**: Activate the focused item (calls `onEnter` callback, or `el.click()`)
104
- - **Escape**: Call `onEscape` callback
104
+ - **Escape**: Call `onEscape` callback. When a callback is given the event is consumed (`preventDefault` + `stopPropagation`), so a sidebar or dialog listening on `document` does not also close behind the overlay. With no `onEscape`, the key passes through untouched.
105
105
 
106
106
  On each key event, the composable re-queries the container for matching items (`itemSelector`), so it naturally handles dynamic lists where items are added or removed. The focused index is validated against the current item count before every operation to prevent stale-index bugs.
107
107
 
@@ -6,6 +6,17 @@ group: forms
6
6
 
7
7
  # useModificationTracker
8
8
 
9
+ !!! warning "Deprecated — use `useBladeForm`"
10
+ `useBladeForm` owns validation, the dirty flag, the unsaved-changes guard and the
11
+ close prompt together, where this tracks only the dirty flag. It has no callers
12
+ left in the framework or the reference app, and `cli/migrate`'s `use-blade-form`
13
+ transform already flags the `useForm` + `useBeforeUnload` +
14
+ `useModificationTracker` combination for the same move.
15
+
16
+ Its comparator is deliberately not being aligned with `useBladeForm`'s: an
17
+ empty array counts as empty there and not here, and changing that would move
18
+ the dirty flag under anyone still on this composable.
19
+
9
20
  Tracks deep changes to a value and reports whether it has been modified compared to its original (pristine) state. This is the core composable behind the "unsaved changes" detection pattern in vc-shell blades. It handles complex nested objects, arrays, and the common API quirk where `null`, `undefined`, and `""` should be treated as equivalent empty values.
10
21
 
11
22
  The composable maintains two copies of the data: the `pristineValue` (the clean baseline) and the `currentValue` (the working copy bound to form fields). Any deep difference between the two sets `isModified` to `true`.
@@ -110,6 +110,8 @@ The plugin instance is resolved via `inject(PopupPluginKey)`. If called outside
110
110
 
111
111
  Multiple popups can be open simultaneously — they stack with the most recent on top.
112
112
 
113
+ On close, focus returns to the control that opened the popup (WCAG 2.4.3 Focus Order). When that control was never focused, or has since been removed from the page, focus goes to the app workspace instead — never left on `<body>`, which would make the next Tab restart at the top of the document. A popup that deliberately moved focus somewhere else keeps it; this repairs lost focus rather than dictating it.
114
+
113
115
  ## Recipe: Delete Confirmation with Notification
114
116
 
115
117
  ```vue
@@ -8,8 +8,6 @@ group: ui-state
8
8
 
9
9
  Controls the sidebar (left navigation panel) expansion, pinning, hover, and mobile menu state. This composable exposes a unified API for reading and toggling all sidebar states from any component within the VcApp tree. The sidebar has three independent dimensions: **pinned** (user explicitly locked it open, persisted to localStorage), **hover-expanded** (mouse is hovering over the collapsed sidebar on desktop), and **mobile menu** (overlay drawer on small screens). The derived `isExpanded` computed combines pinned and hover states for convenience.
10
10
 
11
- Internally, it delegates to `useMenuExpanded()` to share reactive state with other components like `UserDropdownButton` that also need to know the sidebar state.
12
-
13
11
  ## When to Use
14
12
 
15
13
  - Toggle or read sidebar expanded/collapsed state from any component inside VcApp
@@ -82,6 +80,20 @@ provideSidebarState();
82
80
 
83
81
  <!-- internal:end -->
84
82
 
83
+ ## Storage key
84
+
85
+ The pinned state lives in `localStorage` under an app-scoped key -- the first segment of the URL path, so several vc-shell apps on one domain keep independent sidebars. `sidebarStorageKey()` returns that key, so tests and stories can seed the state before `VcApp` mounts instead of hard-coding the string:
86
+
87
+ ```typescript
88
+ import { sidebarStorageKey } from "@vc-shell/framework";
89
+
90
+ // Served from "/operations/catalog" -> "VC_APP_MENU_EXPANDED_operations"
91
+ localStorage.setItem(sidebarStorageKey(), "true");
92
+
93
+ // Or name the app explicitly
94
+ localStorage.setItem(sidebarStorageKey("operations"), "false");
95
+ ```
96
+
85
97
  ## Recipe: Auto-Close Mobile Menu After Navigation
86
98
 
87
99
  ```vue
@@ -138,10 +150,9 @@ const contentClass = computed(() => (isExpanded.value ? "tw-ml-64" : "tw-ml-16")
138
150
  ## Tips
139
151
 
140
152
  - **Hover delay prevents flicker.** The 200ms delay on `setHoverExpanded(true)` means that quickly passing the mouse over the sidebar does not cause it to flash open. Closing is instant so the sidebar feels responsive.
141
- - **`isPinned` is persisted to localStorage.** If a user pins the sidebar, it stays pinned across page reloads and browser sessions. The key is managed internally by `useMenuExpanded`.
153
+ - **`isPinned` is persisted to localStorage.** If a user pins the sidebar, it stays pinned across page reloads and browser sessions. The key is app-scoped -- `sidebarStorageKey()` returns it.
142
154
  - **Do not call outside VcApp.** If you call `useSidebarState()` in a component that is not a descendant of VcApp (e.g., in a standalone dialog or a separate Vue app instance), it will throw an error.
143
155
 
144
156
  ## Related
145
157
 
146
- - `useMenuExpanded` -- lower-level composable that manages pinned/hover state and localStorage persistence
147
158
  - `VcApp` -- the root component that calls `provideSidebarState()`
@@ -7,15 +7,17 @@ internal: true
7
7
 
8
8
  # useSlowNetworkDetection
9
9
 
10
- Detects slow network conditions and shows a persistent warning notification so users know why the UI is unresponsive. Two detection channels work together: a **proactive** channel reads `navigator.connection.effectiveType` to catch weak connections before any request is made, and a **reactive** channel flags API requests that have been pending for more than 10 seconds. The notification auto-dismisses with a 3-second delay after conditions clear, preventing flicker. When the browser goes fully offline, the slow-network notification is suppressed in favor of the existing offline notification from `useConnectionStatus`.
10
+ Detects slow network conditions and publishes them as the `vc-slow-network` class on `<html>`, which the framework renders as a 2px sweep along the top edge of the viewport. Two detection channels work together: a **proactive** channel reads `navigator.connection.effectiveType` to catch weak connections before any request is made, and a **reactive** channel flags idempotent API requests that have been pending for more than 10 seconds. The class is removed with a 3-second delay after conditions clear, preventing flicker. When the browser goes fully offline, the class is dropped in favor of the offline notification from `useConnectionStatus`.
11
+
12
+ A slow network is a state, not an event, so it is never announced as a toast: a dismissable one reappears on every action that outruns the threshold, and a sticky one sits on the screen for the whole session on a 2g connection (VCST-6045).
11
13
 
12
14
  Like `useConnectionStatus`, this is a module-level singleton — calling it from multiple components shares the same state and listeners.
13
15
 
14
16
  ## When to Use
15
17
 
16
18
  - Reactively check `isSlowNetwork` to show skeleton loaders, disable auto-refresh, or display inline warnings
17
- - The notification is automatic — no extra code needed beyond calling the composable once at app startup (already done in `framework/index.ts`)
18
- - Use `trackRequest` / `untrackRequest` only if you have custom fetch logic outside the standard interceptor (the built-in fetch interceptor already tracks all `/api/*` requests)
19
+ - The `vc-slow-network` class is automatic — no extra code needed beyond calling the composable once at app startup (already done in `framework/index.ts`)
20
+ - Use `trackRequest` / `untrackRequest` only if you have custom fetch logic outside the standard interceptor (the built-in fetch interceptor already tracks idempotent `/api/*` requests)
19
21
  - When NOT to use: for offline detection (use `useConnectionStatus` instead) or for measuring exact latency/bandwidth
20
22
 
21
23
  ## Quick Start
@@ -61,11 +63,12 @@ const { isSlowNetwork } = useSlowNetworkDetection();
61
63
 
62
64
  These are module-private `const` declarations, not exported symbols — do not `import` them. They document the thresholds baked into the composable.
63
65
 
64
- | Name | Value | Purpose |
65
- | --------------------------- | ------------------- | --------------------------------------------------- |
66
- | `SLOW_REQUEST_THRESHOLD_MS` | `10000` | Time before a pending request is considered slow |
67
- | `DISMISS_DELAY_MS` | `3000` | Delay before hiding the notification after recovery |
68
- | `SLOW_EFFECTIVE_TYPES` | `["slow-2g", "2g"]` | Connection types flagged as slow |
66
+ | Name | Value | Purpose |
67
+ | --------------------------- | ------------------- | ------------------------------------------------ |
68
+ | `SLOW_REQUEST_THRESHOLD_MS` | `10000` | Time before a pending request is considered slow |
69
+ | `DISMISS_DELAY_MS` | `3000` | Delay before dropping the class after recovery |
70
+ | `SLOW_EFFECTIVE_TYPES` | `["slow-2g", "2g"]` | Connection types flagged as slow |
71
+ | `SLOW_NETWORK_CLASS` | `"vc-slow-network"` | Class the state is published as, on `<html>` |
69
72
 
70
73
  ## How It Works
71
74
 
@@ -75,15 +78,15 @@ On first call, the composable checks `navigator.connection.effectiveType` (Netwo
75
78
 
76
79
  ### Channel 2: Request timers (reactive)
77
80
 
78
- The fetch interceptor in `framework/core/interceptors/index.ts` calls `trackRequest(id)` before every `/api/*` request and `untrackRequest(id)` in the `finally` block. Each tracked request gets a 10-second timer. If the response arrives in time, the timer is cancelled. If not, `isSlowNetwork` becomes `true` and stays `true` until all slow requests complete.
81
+ The fetch interceptor in `framework/core/interceptors/index.ts` calls `trackRequest(id)` before every idempotent `/api/*` request (GET and HEAD) and `untrackRequest(id)` in the `finally` block. An upload or any other mutation is left out: its duration is the operator's uplink, not a stalled server, and a large file would otherwise claim the network is slow every time. Each tracked request gets a 10-second timer. If the response arrives in time, the timer is cancelled. If not, `isSlowNetwork` becomes `true` and stays `true` until all slow requests complete.
79
82
 
80
- ### Notification lifecycle
83
+ ### Class lifecycle
81
84
 
82
- 1. `isSlowNetwork` becomes `true` → show `notification.warning` (deduplicated by fixed ID, persists until removed)
83
- 2. `isSlowNetwork` becomes `false` → start a 3-second dismiss timer
84
- 3. If `isSlowNetwork` goes back to `true` within those 3 seconds → cancel dismiss, notification stays
85
- 4. If the browser goes offline → immediately hide the slow notification (the offline notification takes over)
86
- 5. If the browser comes back online and `isSlowNetwork` is still `true` → re-show
85
+ 1. `isSlowNetwork` becomes `true` → add `vc-slow-network` to `<html>`, and the bar appears
86
+ 2. `isSlowNetwork` becomes `false` → start a 3-second removal timer
87
+ 3. If `isSlowNetwork` goes back to `true` within those 3 seconds → cancel removal, the class stays
88
+ 4. If the browser goes offline → remove the class at once (the offline notification takes over)
89
+ 5. If the browser comes back online and `isSlowNetwork` is still `true` → add it again
87
90
  <!-- internal:end -->
88
91
 
89
92
  ## Recipe: Custom Slow-Network Behavior in a Blade
@@ -131,8 +134,9 @@ async function fetchFromExternalApi(url: string) {
131
134
  ## Tips
132
135
 
133
136
  - **Singleton by design.** Multiple calls to `useSlowNetworkDetection()` share the same state. No overhead from calling it in many components.
134
- - **The notification cannot stack.** It uses a fixed `notificationId`, so even if multiple requests become slow simultaneously, only one notification is shown.
135
- - **The 3-second dismiss delay prevents flicker.** Without it, a burst of requests completing one-by-one would cause the notification to flash on and off rapidly.
137
+ - **The state cannot stack.** A class is present or it is not, so concurrent slow requests read as one state.
138
+ - **The 3-second removal delay prevents flicker.** Without it, a burst of requests completing one-by-one would toggle the class on and off rapidly.
139
+ - **The bar lives in one CSS rule.** `framework/assets/styles/index.scss` draws it next to the `html.vc-offline` rule, so an app can restyle or suppress it without touching the composable. Under `prefers-reduced-motion` it becomes a static line.
136
140
  - **`navigator.connection` has limited support.** Only Chromium-based browsers support it. Firefox and Safari users will only get the request-timer channel, which is still effective.
137
141
  - **The composable does not block requests.** Unlike the offline guard in the interceptor, slow-network detection is purely informational — it never prevents or delays a fetch.
138
142
 
@@ -171,4 +175,4 @@ trackRequest("my-request-2");
171
175
 
172
176
  - [`useConnectionStatus`](../useConnectionStatus/useConnectionStatus.docs.md) — offline detection (binary online/offline)
173
177
  - [`registerInterceptors`](https://github.com/VirtoCommerce/vc-shell/blob/main/framework/core/interceptors/index.ts) — the fetch wrapper that calls `trackRequest`/`untrackRequest`
174
- - `notification` from `@core/notifications/notification` — the notification system used to display the warning
178
+ - `framework/assets/styles/index.scss` — the top-edge sweep this class renders as, alongside the `html.vc-offline` rule
@@ -350,6 +350,10 @@ function helperFunction() {
350
350
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
351
351
  | `provideToolbarService()` | Creates and provides the toolbar service via Vue injection. Idempotent -- returns existing service if already provided. Cleans up via `onScopeDispose`. |
352
352
 
353
+ !!! note "When you need it"
354
+
355
+ Inside a shell app the framework install already provides the toolbar service, so `provideToolbarService()` returns the existing instance and you do not need to call it. Use it when bootstrapping an isolated tree (stories, tests) that has not installed the framework, the same way as `provideWidgetService()` and `provideDashboardService()`.
356
+
353
357
  ### IToolbarItem
354
358
 
355
359
  | Property | Type | Required | Description |
@@ -41,7 +41,6 @@ Every service follows the same architecture:
41
41
  | **SettingsMenuService** | `createSettingsMenuService()` | `addSettingsMenuItem()` | `ISettingsMenuItem` |
42
42
  | **AppBarWidgetService** | `createAppBarWidgetService()` | `addAppBarWidget()` | `AppBarWidget` |
43
43
  | **AppBarMobileButtonsService** | `createAppBarMobileButtonsService()` | (direct API) | `AppBarButtonContent` |
44
- | **GlobalSearchService** | `createGlobalSearchService()` | (direct API) | per-blade search state |
45
44
  | **LanguageService** | `createLanguageService()` | (direct API) | locale strings |
46
45
 
47
46
  ## Usage
@@ -315,6 +315,7 @@ All visual properties are customizable through CSS custom properties. Each varia
315
315
  - `aria-pressed` reflects the `selected` prop for toggle buttons
316
316
  - Icon-only buttons **must** include `ariaLabel` for screen reader users
317
317
  - Disabled buttons use `pointer-events: none` and `opacity: 0.5` to prevent interaction
318
+ - A button that becomes disabled **while it holds focus** keeps the focus: it switches to `aria-disabled` instead of the native attribute, since a natively disabled element cannot be focused and focus would fall to `<body>` (WCAG 2.4.3). It stays inert, and the user is still on it when the work finishes. Every other disabled button keeps the native attribute and stays out of the tab order.
318
319
  - The `click` event calls `e.preventDefault()` -- use `type="submit"` for form submissions
319
320
 
320
321
  ## Related Components
@@ -50,15 +50,16 @@ const color = ref<string | null>(null);
50
50
 
51
51
  ## Key Props
52
52
 
53
- | Prop | Type | Default | Description |
54
- | ------------------------ | ---------------------- | ----------- | -------------------------------------------------------- |
55
- | `modelValue` | `string \| null` | `undefined` | Color value via `v-model` (hex string or CSS color name) |
56
- | `label` | `string` | -- | Label text above the field |
57
- | `placeholder` | `string` | -- | Placeholder text |
58
- | `clearable` | `boolean` | `false` | Shows a clear button when a value is present |
59
- | `size` | `"default" \| "small"` | `"default"` | Field height variant |
60
- | `error` / `errorMessage` | `boolean` / `string` | -- | Error styling and validation message |
61
- | `disabled` | `boolean` | `false` | Disables the input and color picker |
53
+ | Prop | Type | Default | Description |
54
+ | ------------------------ | ---------------------- | ----------- | ---------------------------------------------------------------------- |
55
+ | `modelValue` | `string \| null` | `undefined` | Color value via `v-model` (hex string or CSS color name) |
56
+ | `label` | `string` | -- | Label text above the field |
57
+ | `ariaLabel` | `string` | -- | Accessible name for the text input when no visible `label` is provided |
58
+ | `placeholder` | `string` | -- | Placeholder text |
59
+ | `clearable` | `boolean` | `false` | Shows a clear button when a value is present |
60
+ | `size` | `"default" \| "small"` | `"default"` | Field height variant |
61
+ | `error` / `errorMessage` | `boolean` / `string` | -- | Error styling and validation message |
62
+ | `disabled` | `boolean` | `false` | Disables the input and color picker |
62
63
 
63
64
  ## Events
64
65
 
@@ -294,26 +294,27 @@ const date = ref<Date | null>(new Date("2025-01-15"));
294
294
 
295
295
  ## Props
296
296
 
297
- | Prop | Type | Default | Description |
298
- | ------------------- | ---------------------------- | ---------------- | ------------------------------------------------------------ |
299
- | `modelValue` | `Date \| string \| null` | `undefined` | Bound value via `v-model` |
300
- | `type` | `"date" \| "datetime-local"` | `"date"` | Date only or date + time mode |
301
- | `label` | `string` | -- | Label text above the field |
302
- | `placeholder` | `string` | auto (localized) | Placeholder text; auto-generated from locale if not provided |
303
- | `hint` | `string` | -- | Helper text below the field |
304
- | `tooltip` | `string` | -- | Tooltip on the label info icon |
305
- | `clearable` | `boolean` | `false` | Shows a clear button when a date is selected |
306
- | `loading` | `boolean` | `false` | Shows a spinning loader icon |
307
- | `size` | `"default" \| "small"` | `"default"` | Field height variant |
308
- | `required` | `boolean` | `false` | Shows a required indicator |
309
- | `error` | `boolean` | `false` | Enables error styling |
310
- | `errorMessage` | `string` | -- | Error message below the field |
311
- | `disabled` | `boolean` | `false` | Disables the date picker |
312
- | `autofocus` | `boolean` | `false` | Focus the field on mount |
313
- | `name` | `string` | `"Field"` | HTML name attribute |
314
- | `multilanguage` | `boolean` | `false` | Shows language badge on the label |
315
- | `currentLanguage` | `string` | -- | Language code for the badge |
316
- | `datePickerOptions` | `VueDatePickerProps` | -- | Pass-through options to VueDatePicker |
297
+ | Prop | Type | Default | Description |
298
+ | ------------------- | ---------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------- |
299
+ | `modelValue` | `Date \| string \| null` | `undefined` | Bound value via `v-model` |
300
+ | `type` | `"date" \| "datetime-local"` | `"date"` | Date only or date + time mode |
301
+ | `label` | `string` | -- | Label text above the field |
302
+ | `ariaLabel` | `string` | -- | Accessible name for the date input when no visible `label` is provided; applied through the library's `ariaLabels.input` option |
303
+ | `placeholder` | `string` | auto (localized) | Placeholder text; auto-generated from locale if not provided |
304
+ | `hint` | `string` | -- | Helper text below the field |
305
+ | `tooltip` | `string` | -- | Tooltip on the label info icon |
306
+ | `clearable` | `boolean` | `false` | Shows a clear button when a date is selected |
307
+ | `loading` | `boolean` | `false` | Shows a spinning loader icon |
308
+ | `size` | `"default" \| "small"` | `"default"` | Field height variant |
309
+ | `required` | `boolean` | `false` | Shows a required indicator |
310
+ | `error` | `boolean` | `false` | Enables error styling |
311
+ | `errorMessage` | `string` | -- | Error message below the field |
312
+ | `disabled` | `boolean` | `false` | Disables the date picker |
313
+ | `autofocus` | `boolean` | `false` | Focus the field on mount |
314
+ | `name` | `string` | `"Field"` | HTML name attribute |
315
+ | `multilanguage` | `boolean` | `false` | Shows language badge on the label |
316
+ | `currentLanguage` | `string` | -- | Language code for the badge |
317
+ | `datePickerOptions` | `VueDatePickerProps` | -- | Pass-through options to VueDatePicker |
317
318
 
318
319
  ## Events
319
320
 
@@ -671,32 +671,33 @@ VcInput blocks the minus key for both `number` and `integer` types. If you need
671
671
 
672
672
  ### Props
673
673
 
674
- | Prop | Type | Default | Description |
675
- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------- |
676
- | `modelValue` | `string \| number \| Date \| null` | `undefined` | Bound value via `v-model` |
677
- | `type` | `"text" \| "password" \| "email" \| "tel" \| "url" \| "time" \| "number" \| "integer" \| "date" \| "datetime-local" \| "color"` | `"text"` | Input type. `date`/`datetime-local` delegate to VcDatePicker; `color` delegates to VcColorInput |
678
- | `label` | `string` | -- | Label text displayed above the field |
679
- | `placeholder` | `string` | -- | Placeholder text inside the field |
680
- | `hint` | `string` | -- | Help text displayed below the field |
681
- | `tooltip` | `string` | -- | Tooltip text shown on hover next to the label |
682
- | `prefix` | `string` | -- | Static text rendered before the input value |
683
- | `suffix` | `string` | -- | Static text rendered after the input value |
684
- | `name` | `string` | `"Field"` | HTML `name` attribute on the native input |
685
- | `clearable` | `boolean` | `false` | Shows a clear (x) button when the field has a value |
686
- | `disabled` | `boolean` | `false` | Disables the input (also inherits from `VcInputGroup` context) |
687
- | `required` | `boolean` | `false` | Adds a red asterisk to the label (visual only, no validation) |
688
- | `loading` | `boolean` | `false` | Shows a spinning loader icon inside the field |
689
- | `autofocus` | `boolean` | `false` | Auto-focuses the input on mount |
690
- | `autocomplete` | `string` | -- | Autofill hint set on the native input (e.g. `"username"`, `"current-password"`) |
691
- | `error` | `boolean` | `false` | Activates error styling (red border and ring) |
692
- | `errorMessage` | `string` | -- | Error text shown below the field; also activates error styling when truthy |
693
- | `debounce` | `string \| number` | -- | Delay in ms before emitting model updates |
694
- | `maxlength` | `string \| number` | `"1024"` | Maximum character length |
695
- | `step` | `string` | `"1"` | Step granularity for number inputs |
696
- | `size` | `"default" \| "small"` | `"default"` | Field height variant (36px / 32px) |
697
- | `multilanguage` | `boolean` | `false` | Shows a language indicator badge on the label |
698
- | `currentLanguage` | `string` | -- | Language code to display in the multilanguage badge |
699
- | `datePickerOptions` | `VueDatePickerProps` | -- | Options forwarded to VcDatePicker (only when `type` is `date` or `datetime-local`) |
674
+ | Prop | Type | Default | Description |
675
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------- |
676
+ | `modelValue` | `string \| number \| Date \| null` | `undefined` | Bound value via `v-model` |
677
+ | `type` | `"text" \| "password" \| "email" \| "tel" \| "url" \| "time" \| "number" \| "integer" \| "date" \| "datetime-local" \| "color"` | `"text"` | Input type. `date`/`datetime-local` delegate to VcDatePicker; `color` delegates to VcColorInput |
678
+ | `label` | `string` | -- | Label text displayed above the field |
679
+ | `placeholder` | `string` | -- | Placeholder text inside the field |
680
+ | `hint` | `string` | -- | Help text displayed below the field |
681
+ | `tooltip` | `string` | -- | Tooltip text shown on hover next to the label |
682
+ | `prefix` | `string` | -- | Static text rendered before the input value |
683
+ | `suffix` | `string` | -- | Static text rendered after the input value |
684
+ | `name` | `string` | `"Field"` | HTML `name` attribute on the native input |
685
+ | `clearable` | `boolean` | `false` | Shows a clear (x) button when the field has a value |
686
+ | `disabled` | `boolean` | `false` | Disables the input (also inherits from `VcInputGroup` context) |
687
+ | `required` | `boolean` | `false` | Adds a red asterisk to the label (visual only, no validation) |
688
+ | `loading` | `boolean` | `false` | Shows a spinning loader icon inside the field |
689
+ | `autofocus` | `boolean` | `false` | Auto-focuses the input on mount |
690
+ | `autocomplete` | `string` | -- | Autofill hint set on the native input (e.g. `"username"`, `"current-password"`) |
691
+ | `ariaLabel` | `string` | -- | Accessible name used when no visible `label` is provided; forwarded to the delegated date picker and color input too |
692
+ | `error` | `boolean` | `false` | Activates error styling (red border and ring) |
693
+ | `errorMessage` | `string` | -- | Error text shown below the field; also activates error styling when truthy |
694
+ | `debounce` | `string \| number` | -- | Delay in ms before emitting model updates |
695
+ | `maxlength` | `string \| number` | `"1024"` | Maximum character length |
696
+ | `step` | `string` | `"1"` | Step granularity for number inputs |
697
+ | `size` | `"default" \| "small"` | `"default"` | Field height variant (36px / 32px) |
698
+ | `multilanguage` | `boolean` | `false` | Shows a language indicator badge on the label |
699
+ | `currentLanguage` | `string` | -- | Language code to display in the multilanguage badge |
700
+ | `datePickerOptions` | `VueDatePickerProps` | -- | Options forwarded to VcDatePicker (only when `type` is `date` or `datetime-local`) |
700
701
 
701
702
  ### Events
702
703
 
@@ -268,6 +268,7 @@ Replace the default dropdown toggle with a custom element using the `button` slo
268
268
  | `name` | `string` | -- | HTML name attribute for the input |
269
269
  | `multilanguage` | `boolean` | `false` | Enable multilanguage editing (forwarded to VcSelect) |
270
270
  | `currentLanguage` | `string` | -- | Active language for multilanguage mode (forwarded to VcSelect) |
271
+ | `ariaLabel` | `string` | -- | Accessible name for the value input when no visible `label` is provided |
271
272
 
272
273
  ## Events
273
274
 
@@ -59,6 +59,7 @@ const open = ref(false);
59
59
  | `contentScrollable` | `boolean` | `true` | Enable scrolling in the content area |
60
60
  | `closeOnClickOutside` | `boolean` | `true` | Close when clicking outside |
61
61
  | `closeOnEscape` | `boolean` | `true` | Close on Escape key |
62
+ | `inert` | `boolean` | `false` | Hide the panel from assistive technology and keyboard focus while something covers it. The panel is teleported, so it does not inherit `inert` from an ancestor — pass it explicitly |
62
63
 
63
64
  ## Events
64
65
 
@@ -143,7 +144,7 @@ The panel's z-index comes from the global `--z-critical-floating-panel` token an
143
144
  - Escape key dismisses the panel (configurable via `closeOnEscape`)
144
145
  - Click-outside detection uses `pointerdown` for reliable z-index handling
145
146
  - Supports nested panels via an internal anchor registry -- child panel clicks do not close parent panels
146
- - Exposes `close()` method via template ref
147
+ - Exposes `close()` and `panelEl` (the teleported panel element) via template ref
147
148
 
148
149
  ## Related Components
149
150
 
@@ -157,7 +158,7 @@ The panel's z-index comes from the global `--z-critical-floating-panel` token an
157
158
  - Click-outside detection uses a `pointerdown` listener on `document` rather than a backdrop overlay. This ensures clicks on high-z-index siblings (sidebar, other teleported panels) are still caught.
158
159
  - `panelAnchorRegistry` (`panel-anchor-registry.ts`) is a `WeakMap<Element, Element | null>` that maps each open panel's DOM element to its anchor element. When `anchorRef` is a floating-ui `VirtualElement`, the registry stores its `contextElement` (the live DOM element backing the virtual reference). This is used to distinguish child panels (whose anchor lives inside this panel) from sibling/parent panels when deciding whether to close on outside click.
159
160
  - Nested VcSelect dropdowns teleported to `<body>` are exempt from click-outside via an ARIA `aria-controls` check: if the clicked `.vc-select__dropdown` has an `id` that matches an `aria-controls` attribute inside this panel, the close is suppressed.
160
- - The panel exposes a `close()` method via `defineExpose` for use with template refs.
161
+ - The panel exposes `close()` and `panelEl` via `defineExpose` for use with template refs. `panelEl` is the teleported panel itself — a call site managing focus needs it to answer "is focus still inside?", and cannot reach it any other way.
161
162
  - `useTeleportTarget` resolves the teleport destination (typically `body`) respecting any custom portal container.
162
163
 
163
164
  <!-- internal:end -->
@@ -234,20 +234,23 @@ Use the `"star-and-text"` or `"text"` variant in table cell slots for a compact
234
234
 
235
235
  ## Props
236
236
 
237
- | Prop | Type | Default | Description |
238
- | ------------- | -------------------------------------- | --------- | ---------------------------------------- |
239
- | `modelValue` | `number` | -- | Current rating value to display |
240
- | `max` | `number` | `5` | Maximum rating value (defines the scale) |
241
- | `variant` | `"stars" \| "star-and-text" \| "text"` | `"stars"` | Display variant |
242
- | `label` | `string` | -- | Field label text above the rating |
243
- | `tooltip` | `string` | -- | Tooltip shown on the label |
244
- | `placeholder` | `string` | -- | Text shown when `modelValue` is falsy |
237
+ | Prop | Type | Default | Description |
238
+ | -------------- | -------------------------------------- | --------- | ----------------------------------------------------------------------- |
239
+ | `modelValue` | `number` | -- | Current rating value to display |
240
+ | `max` | `number` | `5` | Maximum rating value (defines the scale) |
241
+ | `variant` | `"stars" \| "star-and-text" \| "text"` | `"stars"` | Display variant |
242
+ | `label` | `string` | -- | Field label text above the rating |
243
+ | `tooltip` | `string` | -- | Tooltip shown on the label |
244
+ | `placeholder` | `string` | -- | Text shown when `modelValue` is falsy |
245
+ | `error` | `boolean` | -- | Marks the field invalid without a message |
246
+ | `errorMessage` | `string` | -- | Validation message shown below the rating; also marks the field invalid |
245
247
 
246
248
  ## Slots
247
249
 
248
- | Slot | Scope | Description |
249
- | --------- | ----- | -------------------------------------------------------------------------------------- |
250
- | `details` | -- | Additional content below the rating (available in `star-and-text` and `text` variants) |
250
+ | Slot | Scope | Description |
251
+ | --------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------- |
252
+ | `details` | -- | Additional content below the rating (available in `star-and-text` and `text` variants) |
253
+ | `error` | -- | Replaces the default validation message element. Keep the slotted element's `id`, or the control's `aria-describedby` points at nothing |
251
254
 
252
255
  ## CSS Variables
253
256
 
@@ -266,6 +269,8 @@ Use the `"star-and-text"` or `"text"` variant in table cell slots for a compact
266
269
  - When no value is present, the `aria-label` falls back to the placeholder text or "No rating".
267
270
  - The label is rendered via `VcLabel` with optional tooltip, maintaining consistent label semantics.
268
271
  - Star icons are decorative and do not receive focus since the component is display-only.
272
+ - A validation message is rendered below the rating and referenced from the control through `aria-describedby`, so a screen reader is told what is wrong rather than only that something is.
273
+ - Invalid state comes from `useFormField`, so it reflects a surrounding `VcInputGroup` as well as the component's own `error` / `errorMessage`.
269
274
 
270
275
  ## Related Components
271
276
 
@@ -251,6 +251,7 @@ const description = ref<string>("");
251
251
  | `name` | `string` | `"Field"` | Field name used for validation/id generation (not applied as an HTML name attribute on the textarea) |
252
252
  | `multilanguage` | `boolean` | `false` | Shows language badge on the label |
253
253
  | `currentLanguage` | `string` | -- | Language code displayed in the badge |
254
+ | `ariaLabel` | `string` | -- | Accessible name used when no visible `label` is provided |
254
255
 
255
256
  ## Events
256
257
 
@@ -79,7 +79,7 @@ VcApp orchestrates several internal systems:
79
79
  1. **Layout** -- switches between `DesktopLayout` (sidebar) and `MobileLayout` (top bar) based on viewport width. The breakpoint is managed by the framework's responsive detection system.
80
80
  2. **Blade Navigation** -- renders `VcBladeNavigation` for the stacked panel workspace. Blades open to the right and scroll horizontally.
81
81
  3. **Module Loading** -- consumes `DynamicModulesKey` for runtime module registration; shows error notifications on failure. Modules declare routes, menu items, and services.
82
- 4. **Services** -- bootstraps shell services (Menu, Toolbar, Settings, Dashboard, GlobalSearch, etc.) via `useShellBootstrap`. These services are available to all child components through provide/inject.
82
+ 4. **Services** -- provides the shell-scoped services via `useShellBootstrap`: dashboard, app bar mobile buttons, and the AI agent when configured. Menu, toolbar, widget and settings services are provided earlier, by the framework install. All are reachable from child components through provide/inject.
83
83
 
84
84
  <!-- internal:end -->
85
85