@vc-shell/vc-app-skill 2.5.0-pr354.da09837 → 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.
- package/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/blade-navigation/blade-nav-composables.docs.md +28 -1
- package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +1 -1
- package/runtime/knowledge/docs/core/composables/useKeyboardNavigation/useKeyboardNavigation.docs.md +8 -8
- package/runtime/knowledge/docs/core/composables/useModificationTracker/useModificationTracker.docs.md +11 -0
- package/runtime/knowledge/docs/core/composables/usePopup/usePopup.docs.md +2 -0
- package/runtime/knowledge/docs/core/composables/useSidebarState/useSidebarState.docs.md +15 -4
- package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +22 -18
- package/runtime/knowledge/docs/core/composables/useToolbar/useToolbar.docs.md +4 -0
- package/runtime/knowledge/docs/core/services/services.docs.md +0 -1
- package/runtime/knowledge/docs/ui/components/atoms/vc-button/vc-button.docs.md +1 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +10 -9
- package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +21 -20
- package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +27 -26
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +1 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-popover/vc-popover.docs.md +3 -2
- package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +16 -11
- package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +1 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-app/vc-app.docs.md +1 -1
- package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +77 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.docs.md +20 -19
- package/runtime/knowledge/docs/ui/components/organisms/vc-scheduler/vc-scheduler.docs.md +4 -2
- 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.
|
|
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.
|
|
1
|
+
2.6.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Synced from framework at commit
|
|
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,
|
|
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
|
|
package/runtime/knowledge/docs/core/composables/useKeyboardNavigation/useKeyboardNavigation.docs.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
18
|
-
- Use `trackRequest` / `untrackRequest` only if you have custom fetch logic outside the standard interceptor (the built-in fetch interceptor already tracks
|
|
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
|
|
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
|
-
###
|
|
83
|
+
### Class lifecycle
|
|
81
84
|
|
|
82
|
-
1. `isSlowNetwork` becomes `true` →
|
|
83
|
-
2. `isSlowNetwork` becomes `false` → start a 3-second
|
|
84
|
-
3. If `isSlowNetwork` goes back to `true` within those 3 seconds → cancel
|
|
85
|
-
4. If the browser goes offline →
|
|
86
|
-
5. If the browser comes back online and `isSlowNetwork` is still `true` →
|
|
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
|
|
135
|
-
- **The 3-second
|
|
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
|
-
- `
|
|
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
|
package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md
CHANGED
|
@@ -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
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
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
|
|
package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md
CHANGED
|
@@ -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
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
305
|
-
| `
|
|
306
|
-
| `
|
|
307
|
-
| `
|
|
308
|
-
| `
|
|
309
|
-
| `
|
|
310
|
-
| `
|
|
311
|
-
| `
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
314
|
-
| `
|
|
315
|
-
| `
|
|
316
|
-
| `
|
|
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
|
-
| `
|
|
692
|
-
| `
|
|
693
|
-
| `
|
|
694
|
-
| `
|
|
695
|
-
| `
|
|
696
|
-
| `
|
|
697
|
-
| `
|
|
698
|
-
| `
|
|
699
|
-
| `
|
|
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
|
|
package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md
CHANGED
|
@@ -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()`
|
|
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
|
|
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
|
|
238
|
-
|
|
|
239
|
-
| `modelValue`
|
|
240
|
-
| `max`
|
|
241
|
-
| `variant`
|
|
242
|
-
| `label`
|
|
243
|
-
| `tooltip`
|
|
244
|
-
| `placeholder`
|
|
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** --
|
|
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
|
|
|
@@ -211,6 +211,83 @@ For any rendering not covered by built-in types, use the `#body` slot:
|
|
|
211
211
|
</VcColumn>
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
### Custom Cell Types
|
|
215
|
+
|
|
216
|
+
A `#body` slot covers one-off formatting. When the same rendering should apply
|
|
217
|
+
to a column `type` across every table in the app, register a cell component
|
|
218
|
+
instead. Registration is global and reactive — already-mounted tables pick the
|
|
219
|
+
new component up.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
// src/cells/register.ts — imported once from your app entry point
|
|
223
|
+
import { useCellRegistry } from "@vc-shell/framework";
|
|
224
|
+
import CellRating from "./CellRating.vue";
|
|
225
|
+
|
|
226
|
+
const { register } = useCellRegistry();
|
|
227
|
+
|
|
228
|
+
register({
|
|
229
|
+
type: "rating",
|
|
230
|
+
component: CellRating,
|
|
231
|
+
config: { editable: false },
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```vue
|
|
236
|
+
<VcColumn id="rating" field="rating" title="Rating" type="rating" />
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The cell component receives a `value` prop holding the resolved field value.
|
|
240
|
+
When `config.editable` is `true`, it also receives `editable`, `label`,
|
|
241
|
+
`fieldName`, `fieldId`, `rules` and `rowIndex` while the row is being edited —
|
|
242
|
+
mirror `CellDefault.vue` for that contract.
|
|
243
|
+
|
|
244
|
+
Registering an existing type replaces the built-in for the whole app:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
register({ type: "money", component: MyMoneyCell, config: { editable: true } });
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
The 12 built-ins are registered by `registerBuiltinCells()`, which runs once
|
|
251
|
+
when the table's cell renderer module is loaded — that is, as soon as anything
|
|
252
|
+
imports `VcDataTable`. Because ES modules finish evaluating before the module
|
|
253
|
+
that imported them runs, that always happens before your own registration code,
|
|
254
|
+
and `register()` is last-write-wins — so a type you register replaces the
|
|
255
|
+
built-in. `registerBuiltinCells()` itself skips types that are already present,
|
|
256
|
+
which keeps it idempotent and stops it clobbering a registration that did manage
|
|
257
|
+
to land first (for instance when the table is loaded through a dynamic
|
|
258
|
+
`import()`). `BUILTIN_CELL_TYPES` lists the names that are taken.
|
|
259
|
+
|
|
260
|
+
A `type` that resolves to nothing falls back to `CellDefault`, which renders the
|
|
261
|
+
raw value. Custom names are allowed, so a misspelt built-in is not a type error;
|
|
262
|
+
in development builds an unresolved type logs a warning naming the type.
|
|
263
|
+
|
|
264
|
+
#### Cell registry API
|
|
265
|
+
|
|
266
|
+
| Member | Signature | Description |
|
|
267
|
+
| -------------------- | ------------------------------------------------- | -------------------------------------------------------- |
|
|
268
|
+
| `register` | `(registration: CellRegistration) => void` | Register or replace a cell type. |
|
|
269
|
+
| `get` | `(type: string) => CellRegistration \| undefined` | Look up a registration. |
|
|
270
|
+
| `has` | `(type: string) => boolean` | Whether a type is registered. |
|
|
271
|
+
| `getRegisteredTypes` | `() => string[]` | All registered type names. |
|
|
272
|
+
| `unregister` | `(type: string) => boolean` | Remove a type. Returns `false` if it was not registered. |
|
|
273
|
+
| `clear` | `() => void` | Remove every registration, including the built-ins. |
|
|
274
|
+
|
|
275
|
+
Two module-level exports come with it:
|
|
276
|
+
|
|
277
|
+
| Export | Signature | Description |
|
|
278
|
+
| ---------------------- | --------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
279
|
+
| `registerBuiltinCells` | `() => void` | Register the 12 built-in cell types. Idempotent and non-destructive; called for you when the table loads. |
|
|
280
|
+
| `BUILTIN_CELL_TYPES` | `readonly CellType[]` | The names of the 12 built-in types. |
|
|
281
|
+
|
|
282
|
+
`CellRegistration` is `{ type: CellType | string; component: Component; config?: { editable?: boolean; additionalProps?: string[] } }`.
|
|
283
|
+
|
|
284
|
+
`clear()` empties the registry permanently — `registerBuiltinCells()` runs once
|
|
285
|
+
per module load, so it will not repopulate on its own. Call it again yourself if
|
|
286
|
+
you need the built-ins back.
|
|
287
|
+
|
|
288
|
+
`CellType` accepts the 12 built-in names with autocomplete plus any custom
|
|
289
|
+
string. `BuiltinCellType` is the narrow union of just the built-ins.
|
|
290
|
+
|
|
214
291
|
---
|
|
215
292
|
|
|
216
293
|
## Sorting
|
|
@@ -46,25 +46,26 @@ The prop names match the `DynamicObjectProperty` shape returned by the platform
|
|
|
46
46
|
|
|
47
47
|
## Key Props
|
|
48
48
|
|
|
49
|
-
| Prop | Type | Default | Description
|
|
50
|
-
| -------------------- | ---------------------------- | ---------- |
|
|
51
|
-
| `property` | `T` | _required_ | Property object with `id` and metadata
|
|
52
|
-
| `modelValue` | `any` | _required_ | Current property value (v-model)
|
|
53
|
-
| `valueType` | `string` | _required_ | Type: ShortText, LongText, Number, Integer, DateTime, Boolean, Measure, Color
|
|
54
|
-
| `name` | `string` | _required_ | Property name for display and field identification
|
|
55
|
-
| `required` | `boolean` | _required_ | Whether the field is required
|
|
56
|
-
| `disabled` | `boolean` | `false` | Disables the input
|
|
57
|
-
| `dictionary` | `boolean` | `false` | Uses VcSelect/VcMultivalue with options
|
|
58
|
-
| `multivalue` | `boolean` | `false` | Supports multiple values
|
|
59
|
-
| `multilanguage` | `boolean` | `false` | Supports localized values
|
|
60
|
-
| `currentLanguage` | `string` | - | Active locale used for localized values
|
|
61
|
-
| `optionsGetter` | `Function` | _required_ | Async loader for dictionary options, called as `(propertyId, keyword?, locale?)`
|
|
62
|
-
| `measurementsGetter` | `Function` | - | Async loader for measurement units
|
|
63
|
-
| `optionsValue` | `string` | `"id"` | Field on each option used as its value (VcSelect mapping)
|
|
64
|
-
| `optionsLabel` | `string` | `"value"` | Inert. The option label is fixed to `"value"` when `multilanguage` is true and `"alias"` otherwise; only `optionsValue` is honored for VcSelect mapping.
|
|
65
|
-
| `displayNames` | `{ name?; languageCode? }[]` | - | Localized display names used to resolve the field label per locale
|
|
66
|
-
| `placeholder` | `string` | - | Placeholder text for the input
|
|
67
|
-
| `rules` | `object` | - | Validation rules: `{ min, max, regex }`
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
| -------------------- | ---------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `property` | `T` | _required_ | Property object with `id` and metadata |
|
|
52
|
+
| `modelValue` | `any` | _required_ | Current property value (v-model) |
|
|
53
|
+
| `valueType` | `string` | _required_ | Type: ShortText, LongText, Number, Integer, DateTime, Boolean, Measure, Color |
|
|
54
|
+
| `name` | `string` | _required_ | Property name for display and field identification |
|
|
55
|
+
| `required` | `boolean` | _required_ | Whether the field is required |
|
|
56
|
+
| `disabled` | `boolean` | `false` | Disables the input |
|
|
57
|
+
| `dictionary` | `boolean` | `false` | Uses VcSelect/VcMultivalue with options |
|
|
58
|
+
| `multivalue` | `boolean` | `false` | Supports multiple values |
|
|
59
|
+
| `multilanguage` | `boolean` | `false` | Supports localized values |
|
|
60
|
+
| `currentLanguage` | `string` | - | Active locale used for localized values |
|
|
61
|
+
| `optionsGetter` | `Function` | _required_ | Async loader for dictionary options, called as `(propertyId, keyword?, locale?)` |
|
|
62
|
+
| `measurementsGetter` | `Function` | - | Async loader for measurement units |
|
|
63
|
+
| `optionsValue` | `string` | `"id"` | Field on each option used as its value (VcSelect mapping) |
|
|
64
|
+
| `optionsLabel` | `string` | `"value"` | Inert. The option label is fixed to `"value"` when `multilanguage` is true and `"alias"` otherwise; only `optionsValue` is honored for VcSelect mapping. |
|
|
65
|
+
| `displayNames` | `{ name?; languageCode? }[]` | - | Localized display names used to resolve the field label per locale |
|
|
66
|
+
| `placeholder` | `string` | - | Placeholder text for the input |
|
|
67
|
+
| `rules` | `object` | - | Validation rules: `{ min, max, regex }` |
|
|
68
|
+
| `hideLabel` | `boolean` | `false` | Hides the control's own visible label so the consumer can render its own label row (e.g. to place an action next to the property name). Validation and the placeholder fallback are unaffected. The display name is forwarded as the control's accessible name for nine control families: short text, number, integer, long text, boolean, datetime, colour, Measure, and the fallback used for an unrecognized `valueType`. Dictionary and multivalue properties (VcSelect, VcMultivalue) are **not** covered — their focusable triggers carry no accessible name, so those properties still need a visible label. |
|
|
68
69
|
|
|
69
70
|
::storybook id="data-display-vcdynamicproperty--property-form" height="500"
|
|
70
71
|
|
|
@@ -162,6 +162,8 @@ With `editable`, `VcScheduler` ships a complete built-in create/edit flow -- no
|
|
|
162
162
|
|
|
163
163
|
The editor modal additionally exposes all-day toggle, start/end date-time, and color, plus a Delete action in edit mode (commits via `event-delete`). Saving from either surface emits the public `event-create`/`event-update` event -- see [Events](#events) for the exact payloads.
|
|
164
164
|
|
|
165
|
+
For an **all-day** event the editor's End field is the last day the event covers, matching what the chip and the quick-info popover announce -- so a one-day event has End equal to Start. The `end` in the emitted payload is still the exclusive midnight boundary the layout works in; the editor converts on save. An End before Start is refused rather than saved.
|
|
166
|
+
|
|
165
167
|
This entire flow is internal state: no `v-model` or extra event wiring is needed to make it work, beyond `editable` and the `event-create`/`event-update`/`event-delete` handlers you already have for persisting the result.
|
|
166
168
|
|
|
167
169
|
### `editorMode`: opting out of the built-in UI
|
|
@@ -498,8 +500,8 @@ const events = [{ id: "a", title: "Promo", start, end, allDay: true, color: "#a2
|
|
|
498
500
|
- The Month grid uses `role="grid"` / `role="row"` / `role="gridcell"` / `role="columnheader"` for the weekday header and day cells, each `gridcell` carrying a full formatted-date `aria-label`.
|
|
499
501
|
- Each event bar and timed chip is a focusable `role="button"` element (`tabindex="0"`) with an `aria-label` built from its title and formatted start/end dates -- activate with click or `Enter`. For an all-day event the announced end is the **last inclusive day**, not the exclusive `end` you pass: an event with `end` at midnight runs through the previous day, so a one-day event is announced as a single date rather than a two-day range. Timed events are announced with their real times.
|
|
500
502
|
- The "+N more" overflow control is a `<button>` whose visible text is its accessible name; the date it belongs to comes from the enclosing `gridcell`. Activating it moves focus to the first event in the popover, and closing the popover (Escape, or the close button) returns focus to the "+N more" button. Each event inside the popover is a `<button>` too, so a day's 4th and later events are reachable and operable by keyboard.
|
|
501
|
-
-
|
|
502
|
-
- The editor modal and the This event/All events scope dialog are `VcPopup`, which is a Headless UI `Dialog` -- those do get accessible naming, a focus trap, focus restore and `inert` on the background from the library.
|
|
503
|
+
- All three anchored popovers -- "+N more" overflow, quick-info and quick-create -- are `VcPopover` panels carrying `role="dialog"` and an accessible name (the formatted date, the event title, and "New event" respectively). They do **not** trap focus: `VcPopover` has no focus management of its own, so each one moves focus into itself on open and hands it back to whatever opened it on close. Focus is repaired, not dictated -- a user who moves focus out of the panel keeps it there when the panel closes.
|
|
504
|
+
- The editor modal and the This event/All events scope dialog are `VcPopup`, which is a Headless UI `Dialog` -- those do get accessible naming, a focus trap, focus restore and `inert` on the background from the library.
|
|
503
505
|
- The recurrence editor's weekday toggle buttons expose `aria-pressed` (selected state) and an `aria-label` with the full weekday name, since their visible narrow labels ("S", "M", "T", ...) collide (Sun/Sat, Tue/Thu). The "↻" recurring-occurrence marker is `aria-hidden` -- purely visual, redundant with the event's own accessible name.
|
|
504
506
|
- The toolbar's prev/next buttons carry an explicit `aria-label` ("Previous" / "Next") since they are icon-only; the today/view-switch buttons show text labels.
|
|
505
507
|
- Event move/resize (Month view's `editable`) is a pointer-drag interaction with no keyboard equivalent yet; use a form outside VcScheduler for keyboard-only date edits. Timeline view is read-only (click still opens the quick-info popover).
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: useMenuExpanded
|
|
3
|
-
category: composables
|
|
4
|
-
group: ui-state
|
|
5
|
-
slug: useMenuExpanded
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# useMenuExpanded
|
|
9
|
-
|
|
10
|
-
Manages the sidebar menu expanded/collapsed and hover-expanded state with localStorage persistence. This is the low-level composable that powers the sidebar pin/unpin behavior in the vc-shell admin UI. It tracks two independent states: the permanent "pinned" state (persisted across sessions) and the transient "hover-expanded" state (active only while the user hovers over a collapsed sidebar).
|
|
11
|
-
|
|
12
|
-
## When to Use
|
|
13
|
-
|
|
14
|
-
- Low-level control over sidebar pin and hover state
|
|
15
|
-
- Building a custom sidebar component that needs expand/collapse persistence
|
|
16
|
-
- When NOT to use: prefer `useSidebarState` which wraps this composable and adds mobile menu support, derived `isExpanded` computed, and responsive breakpoint handling
|
|
17
|
-
|
|
18
|
-
## Basic Usage
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
import { useMenuExpanded } from "@vc-shell/framework";
|
|
22
|
-
|
|
23
|
-
const { isExpanded, toggleExpanded, isHoverExpanded, toggleHoverExpanded } = useMenuExpanded();
|
|
24
|
-
|
|
25
|
-
// Toggle pin state (persisted to localStorage)
|
|
26
|
-
toggleExpanded();
|
|
27
|
-
|
|
28
|
-
// Hover expand with 200ms delay
|
|
29
|
-
toggleHoverExpanded(true); // opens after delay
|
|
30
|
-
toggleHoverExpanded(false); // closes immediately
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## API
|
|
34
|
-
|
|
35
|
-
### Returns
|
|
36
|
-
|
|
37
|
-
| Property | Type | Description |
|
|
38
|
-
| --------------------- | ---------------------------------- | ---------------------------------------------------------------- |
|
|
39
|
-
| `isExpanded` | `Ref<boolean>` | Pinned state, persisted via `useLocalStorage` |
|
|
40
|
-
| `toggleExpanded` | `() => void` | Toggle the pinned state |
|
|
41
|
-
| `isHoverExpanded` | `Ref<boolean>` | Temporary hover expansion (not persisted) |
|
|
42
|
-
| `toggleHoverExpanded` | `(shouldExpand?: boolean) => void` | Set hover state; opening has a 200ms delay, closing is immediate |
|
|
43
|
-
|
|
44
|
-
## Recipe: Custom Sidebar with Hover Expand
|
|
45
|
-
|
|
46
|
-
A common pattern is binding mouse events on the sidebar rail so the menu previews its full width on hover, without permanently pinning it open:
|
|
47
|
-
|
|
48
|
-
```vue
|
|
49
|
-
<script setup lang="ts">
|
|
50
|
-
import { computed } from "vue";
|
|
51
|
-
import { useMenuExpanded } from "@vc-shell/framework";
|
|
52
|
-
|
|
53
|
-
const { isExpanded, toggleExpanded, isHoverExpanded, toggleHoverExpanded } = useMenuExpanded();
|
|
54
|
-
|
|
55
|
-
// The sidebar is visually expanded if pinned OR hover-expanded
|
|
56
|
-
const isVisuallyExpanded = computed(() => isExpanded.value || isHoverExpanded.value);
|
|
57
|
-
</script>
|
|
58
|
-
|
|
59
|
-
<template>
|
|
60
|
-
<aside
|
|
61
|
-
:class="{ 'sidebar--expanded': isVisuallyExpanded }"
|
|
62
|
-
@mouseenter="toggleHoverExpanded(true)"
|
|
63
|
-
@mouseleave="toggleHoverExpanded(false)"
|
|
64
|
-
>
|
|
65
|
-
<nav>
|
|
66
|
-
<!-- menu items -->
|
|
67
|
-
</nav>
|
|
68
|
-
<button @click="toggleExpanded">
|
|
69
|
-
{{ isExpanded ? "Unpin" : "Pin" }}
|
|
70
|
-
</button>
|
|
71
|
-
</aside>
|
|
72
|
-
</template>
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
<!-- internal:start -->
|
|
76
|
-
|
|
77
|
-
## Details
|
|
78
|
-
|
|
79
|
-
- **Storage key scoping**: The key is scoped per application using the first URL path segment: `VC_APP_MENU_EXPANDED_{appName}`. For example, if the app is hosted at `/operations/`, the key is `VC_APP_MENU_EXPANDED_operations`. This allows multiple vc-shell apps on the same domain to maintain independent sidebar states.
|
|
80
|
-
- **Hover delay**: Opening uses a 200ms debounce to prevent accidental expansion when the cursor briefly passes over the sidebar. Closing is immediate to feel responsive.
|
|
81
|
-
- **Cleanup**: Pending hover timeouts are cleaned up via `onScopeDispose` to prevent memory leaks when the composable's effect scope is destroyed.
|
|
82
|
-
- **Default state**: The sidebar starts pinned open (`true`) on first visit, which is the most user-friendly default for new users.
|
|
83
|
-
<!-- internal:end -->
|
|
84
|
-
|
|
85
|
-
## Tips
|
|
86
|
-
|
|
87
|
-
- If you call `toggleHoverExpanded(true)` and then `toggleHoverExpanded(false)` within the 200ms window, the expansion is canceled -- the timeout is cleared before it fires.
|
|
88
|
-
- The composable does not handle mobile breakpoints. For responsive behavior, use `useSidebarState` instead, which combines `useMenuExpanded` with mobile detection.
|
|
89
|
-
- Each call to `useMenuExpanded()` creates a new instance with its own timeout tracking, but they share the same localStorage-backed `isExpanded` ref (via `useLocalStorage`). Multiple instances will stay in sync for the pinned state.
|
|
90
|
-
|
|
91
|
-
## Related
|
|
92
|
-
|
|
93
|
-
- `useSidebarState` -- higher-level composable that adds mobile menu and derived `isExpanded` computed
|