@vc-shell/vc-app-skill 2.0.0-alpha.29 → 2.0.0-alpha.31
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +76 -0
- package/runtime/knowledge/docs/core/composables/useDynamicProperties/useDynamicProperties.docs.md +104 -105
- package/runtime/knowledge/docs/core/composables/usePopup/usePopup.docs.md +195 -0
- package/runtime/knowledge/docs/core/composables/useResponsive/useResponsive.docs.md +178 -0
- package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +4 -4
- package/runtime/knowledge/docs/ui/components/atoms/vc-image/vc-image.docs.md +1 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-image-tile/vc-image-tile.docs.md +1 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +27 -2
- package/runtime/knowledge/docs/ui/components/organisms/vc-gallery/vc-gallery.docs.md +55 -16
- package/runtime/knowledge/docs/core/composables/useBladeContext.docs.md +0 -111
- package/runtime/knowledge/docs/core/composables/useBladeWidgets.docs.md +0 -305
- package/runtime/knowledge/docs/core/composables/useGlobalSearch/useGlobalSearch.docs.md +0 -146
- package/runtime/knowledge/docs/core/composables/useMenuExpanded.docs.md +0 -83
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
# [2.0.0-alpha.31](https://github.com/VirtoCommerce/vc-shell/compare/v2.0.0-alpha.30...v2.0.0-alpha.31) (2026-04-01)
|
|
2
|
+
|
|
3
|
+
**Note:** Version bump only for package @vc-shell/vc-app-skill
|
|
4
|
+
|
|
5
|
+
# [2.0.0-alpha.30](https://github.com/VirtoCommerce/vc-shell/compare/v2.0.0-alpha.29...v2.0.0-alpha.30) (2026-03-30)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **docs:** add documentation for usePopup and useResponsive composables ([342a50a](https://github.com/VirtoCommerce/vc-shell/commit/342a50ada1545637782e01f5452a60839aa90fd2))
|
|
1
11
|
# [2.0.0-alpha.29](https://github.com/VirtoCommerce/vc-shell/compare/v2.0.0-alpha.28...v2.0.0-alpha.29) (2026-03-26)
|
|
2
12
|
|
|
3
13
|
**Note:** Version bump only for package @vc-shell/vc-app-skill
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vc-shell/vc-app-skill",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.31",
|
|
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.0.0-alpha.
|
|
1
|
+
2.0.0-alpha.31
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Synced from framework at commit
|
|
1
|
+
Synced from framework at commit 2f71da38b on 2026-04-01T16:03:16.012Z
|
|
@@ -123,6 +123,14 @@ These are reactive `ComputedRef` values that reflect the current blade's state.
|
|
|
123
123
|
| `setError` | `(error: unknown) => void` | Display an error banner on the blade |
|
|
124
124
|
| `clearError` | `() => void` | Clear the blade error banner |
|
|
125
125
|
|
|
126
|
+
#### Banner Management (blade context required)
|
|
127
|
+
|
|
128
|
+
| Method | Signature | Description |
|
|
129
|
+
|----------------|--------------------------------------------------------------|----------------------------------------------------------------|
|
|
130
|
+
| `addBanner` | `(options: Omit<IBladeBanner, "id" \| "_system">) => string` | Add a custom banner to the blade. Returns the banner's unique ID. |
|
|
131
|
+
| `removeBanner` | `(id: string) => void` | Remove a specific banner by its ID |
|
|
132
|
+
| `clearBanners` | `() => void` | Remove all custom banners (system error/modified banners are preserved) |
|
|
133
|
+
|
|
126
134
|
#### Lifecycle Hooks (blade context required)
|
|
127
135
|
|
|
128
136
|
| Method | Signature | Description |
|
|
@@ -373,6 +381,74 @@ async function loadData() {
|
|
|
373
381
|
</script>
|
|
374
382
|
```
|
|
375
383
|
|
|
384
|
+
### Banner Management
|
|
385
|
+
|
|
386
|
+
Add custom banners (info, warning, danger, success) to the top of a blade. Banners appear between the header and toolbar, sorted by severity: danger > warning > info > success.
|
|
387
|
+
|
|
388
|
+
```vue
|
|
389
|
+
<script setup lang="ts">
|
|
390
|
+
import { h } from "vue";
|
|
391
|
+
import { useBlade } from "@vc-shell/framework";
|
|
392
|
+
|
|
393
|
+
const { addBanner, removeBanner, clearBanners } = useBlade();
|
|
394
|
+
|
|
395
|
+
// Simple text banner
|
|
396
|
+
const bannerId = addBanner({
|
|
397
|
+
variant: "info",
|
|
398
|
+
message: "This record is in read-only mode",
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Warning with dismiss button
|
|
402
|
+
addBanner({
|
|
403
|
+
variant: "warning",
|
|
404
|
+
message: "License expires in 7 days",
|
|
405
|
+
dismissible: true,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Banner with an action button
|
|
409
|
+
addBanner({
|
|
410
|
+
variant: "success",
|
|
411
|
+
message: "Import completed (42 items)",
|
|
412
|
+
dismissible: true,
|
|
413
|
+
action: {
|
|
414
|
+
label: "View report",
|
|
415
|
+
handler: () => openReport(),
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Banner with custom render function
|
|
420
|
+
addBanner({
|
|
421
|
+
variant: "info",
|
|
422
|
+
render: () => h("span", [
|
|
423
|
+
"Data synced from ",
|
|
424
|
+
h("b", "Warehouse A"),
|
|
425
|
+
" at 14:32",
|
|
426
|
+
]),
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Remove a specific banner
|
|
430
|
+
removeBanner(bannerId);
|
|
431
|
+
|
|
432
|
+
// Clear all custom banners (system error/modified banners are preserved)
|
|
433
|
+
clearBanners();
|
|
434
|
+
</script>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### IBladeBanner
|
|
438
|
+
|
|
439
|
+
The options object passed to `addBanner`:
|
|
440
|
+
|
|
441
|
+
| Property | Type | Default | Description |
|
|
442
|
+
|---------------|-----------------------------------|----------|----------------------------------------------------|
|
|
443
|
+
| `variant` | `"danger" \| "warning" \| "info" \| "success"` | required | Color scheme and default icon |
|
|
444
|
+
| `message` | `string` | -- | Plain text message |
|
|
445
|
+
| `render` | `() => VNode` | -- | Custom render function (takes priority over message) |
|
|
446
|
+
| `dismissible` | `boolean` | `false` | Show a close button to dismiss the banner |
|
|
447
|
+
| `icon` | `string` | -- | Override the default variant icon (lucide icon name) |
|
|
448
|
+
| `action` | `{ label: string; handler: () => void }` | -- | Action button displayed on the right side |
|
|
449
|
+
|
|
450
|
+
> **Note:** `addBanner` returns a unique string ID. Use it with `removeBanner(id)` to programmatically remove a specific banner. `clearBanners()` removes all custom banners but preserves system banners (error and unsaved changes).
|
|
451
|
+
|
|
376
452
|
---
|
|
377
453
|
|
|
378
454
|
## Blade Context: defineBladeContext / injectBladeContext
|
package/runtime/knowledge/docs/core/composables/useDynamicProperties/useDynamicProperties.docs.md
CHANGED
|
@@ -1,163 +1,162 @@
|
|
|
1
1
|
# useDynamicProperties
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Composable for managing dynamic (runtime-defined) property values with support for multilanguage, multivalue, dictionary, color, and measurement property types.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Internally uses a strategy pattern — each property type (regular, boolean, dictionary, measure, color) has a dedicated handler with `get`/`set` methods.
|
|
6
6
|
|
|
7
7
|
## When to Use
|
|
8
8
|
|
|
9
9
|
- In product, category, or any entity detail blades that display platform dynamic properties
|
|
10
10
|
- When properties are defined at runtime (not compile-time) and may be multilanguage or dictionary-based
|
|
11
11
|
- When you need to render and edit property values that can be text, boolean, number, datetime, dictionary selection, color picker, or measurement with units
|
|
12
|
-
- When NOT to use: for static, compile-time form fields, use standard Vue reactive state
|
|
12
|
+
- When NOT to use: for static, compile-time form fields, use standard Vue reactive state
|
|
13
13
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
|
-
import { useDynamicProperties } from
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
(
|
|
23
|
-
|
|
24
|
-
PropertyDictionaryItem,
|
|
25
|
-
(measureId, locale) => api.searchMeasurements(measureId, locale),
|
|
26
|
-
);
|
|
17
|
+
import { useDynamicProperties } from "@vc-shell/framework";
|
|
18
|
+
|
|
19
|
+
const { getPropertyValue, setPropertyValue, loadDictionaries, loadMeasurements, loading } =
|
|
20
|
+
useDynamicProperties({
|
|
21
|
+
searchDictionary: (criteria) => api.searchPropertyDictionaryItems(criteria),
|
|
22
|
+
searchMeasurements: (measureId, locale) => api.searchMeasurements(measureId, locale),
|
|
23
|
+
});
|
|
27
24
|
```
|
|
28
25
|
|
|
29
26
|
## API
|
|
30
27
|
|
|
31
|
-
###
|
|
28
|
+
### Options
|
|
32
29
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `PropertyDictionaryItemConstructor` | `new (data?) => TPropertyDictionaryItem` | Yes | Constructor class for creating dictionary item instances. Used when localizing dictionary items. |
|
|
38
|
-
| `searchMeasurementFunction` | `(measureId, locale?) => Promise<TMeasurement[] \| undefined>` | No | API function for loading measurement/unit-of-measure dictionaries. Only needed if you have measure-type properties. |
|
|
30
|
+
| Option | Type | Required | Description |
|
|
31
|
+
|--------|------|----------|-------------|
|
|
32
|
+
| `searchDictionary` | `(criteria: IBasePropertyDictionaryItemSearchCriteria) => Promise<IBasePropertyDictionaryItem[] \| undefined>` | Yes | API function to search dictionary items by property ID and keyword |
|
|
33
|
+
| `searchMeasurements` | `(measureId: string, locale?: string) => Promise<IBaseMeasurementDictionaryItem[] \| undefined>` | No | API function for loading measurement units. Only needed for measure-type properties |
|
|
39
34
|
|
|
40
35
|
### Returns
|
|
41
36
|
|
|
42
37
|
| Property | Type | Description |
|
|
43
38
|
|----------|------|-------------|
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
39
|
+
| `getPropertyValue` | `(property, locale) => PropertyDisplayValue` | Read display value for a property. Returns string, boolean, or array depending on type. Does NOT mutate the property. |
|
|
40
|
+
| `setPropertyValue` | `(params: SetPropertyValueParams) => void` | Write value to a property. Handles type-specific transformation and empty cleanup. |
|
|
41
|
+
| `loadDictionaries` | `(propertyId, keyword?, locale?) => Promise<...>` | Load dictionary items. If locale is provided, resolves localized values. |
|
|
42
|
+
| `loadMeasurements` | `(measureId, keyword?, locale?) => Promise<...>` | Load measurement units. No-op if `searchMeasurements` was not provided. |
|
|
43
|
+
| `loading` | `ComputedRef<boolean>` | Whether a dictionary lookup is in progress. |
|
|
49
44
|
|
|
50
45
|
### SetPropertyValueParams
|
|
51
46
|
|
|
52
47
|
| Field | Type | Description |
|
|
53
48
|
|-------|------|-------------|
|
|
54
|
-
| `property` | `
|
|
55
|
-
| `value` | `string \|
|
|
56
|
-
| `dictionary` | `
|
|
49
|
+
| `property` | `IBaseProperty` | The property object to update. Modified in place. |
|
|
50
|
+
| `value` | `string \| IBasePropertyValue[] \| (IBasePropertyDictionaryItem & { value: string })[]` | The new value. Type depends on property configuration. |
|
|
51
|
+
| `dictionary` | `IBasePropertyDictionaryItem[]?` | Dictionary items. Required when setting a dictionary value. |
|
|
57
52
|
| `locale` | `string?` | Current locale for multilanguage properties. |
|
|
58
|
-
| `initialProp` | `TProperty?` | Original property state. Used for empty-value detection -- if the original was empty and the new value is empty, a placeholder is preserved; otherwise, the value array is cleared. |
|
|
59
53
|
| `unitOfMeasureId` | `string?` | Unit of measure ID for measure-type properties. |
|
|
60
54
|
| `colorCode` | `string?` | Color hex code for color-type properties. |
|
|
61
55
|
|
|
56
|
+
### PropertyDisplayValue
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
type PropertyDisplayValue = string | IBasePropertyValue[] | boolean;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
62
|
## How It Works
|
|
63
63
|
|
|
64
|
-
###
|
|
64
|
+
### Strategy Resolution
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
The composable resolves a strategy handler based on property flags:
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
| Priority | Condition | Strategy | Handles |
|
|
69
|
+
|----------|-----------|----------|---------|
|
|
70
|
+
| 1 | `valueType === "Measure"` | measureStrategy | Numeric input with unit dropdown |
|
|
71
|
+
| 2 | `valueType === "Color"` && !dictionary | colorStrategy | Color picker, multivalue colors |
|
|
72
|
+
| 3 | `valueType === "Boolean"` | booleanStrategy | Checkbox/switch |
|
|
73
|
+
| 4 | `dictionary === true` | dictionaryStrategy | Select dropdowns with localized options |
|
|
74
|
+
| 5 | (default) | regularStrategy | ShortText, LongText, Number, Integer, DateTime |
|
|
71
75
|
|
|
72
|
-
### Value
|
|
76
|
+
### Value Getting
|
|
73
77
|
|
|
74
|
-
`
|
|
75
|
-
1. **Measure** (`valueType === "Measure"`): Creates a single value entry with `unitOfMeasureId`.
|
|
76
|
-
2. **Color** (`valueType === "Color"`, no dictionary): Creates value entry/entries with `colorCode`.
|
|
77
|
-
3. **Dictionary**: Resolves the selected dictionary item(s), expanding `localizedValues` into per-locale value entries for multilanguage dictionaries.
|
|
78
|
-
4. **Regular**: Handles the remaining cases -- simple text, number, boolean, datetime.
|
|
78
|
+
Each strategy's `get()` reads from `property.values` without mutation:
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
- **Multilanguage**: Finds value matching locale, falls back to value without languageCode
|
|
81
|
+
- **Multivalue**: Returns full array (filtered by locale for multilanguage)
|
|
82
|
+
- **Dictionary**: Returns `valueId` instead of `value`
|
|
83
|
+
- **Boolean**: Returns `false` (not `""`) when no value exists
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
### Value Setting
|
|
83
86
|
|
|
84
|
-
|
|
87
|
+
Each strategy's `set()` writes to `property.values`:
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
import { ref, watch } from 'vue';
|
|
90
|
-
|
|
91
|
-
const { currentLocale } = useLanguages();
|
|
92
|
-
const { loading, loadDictionaries, getPropertyValue, setPropertyValue } =
|
|
93
|
-
useDynamicProperties(searchDictionaryItems, PropertyValue, PropertyDictionaryItem);
|
|
94
|
-
|
|
95
|
-
const properties = ref<Property[]>([]);
|
|
96
|
-
|
|
97
|
-
// Display values, keyed by property ID
|
|
98
|
-
function displayValue(property: Property) {
|
|
99
|
-
return getPropertyValue(property, currentLocale.value);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Handle value change from a form input
|
|
103
|
-
async function onValueChange(property: Property, newValue: string) {
|
|
104
|
-
if (property.dictionary) {
|
|
105
|
-
const dictItems = await loadDictionaries(property.id!, '', currentLocale.value);
|
|
106
|
-
setPropertyValue({
|
|
107
|
-
property,
|
|
108
|
-
value: newValue,
|
|
109
|
-
dictionary: dictItems ?? [],
|
|
110
|
-
locale: currentLocale.value,
|
|
111
|
-
});
|
|
112
|
-
} else {
|
|
113
|
-
setPropertyValue({
|
|
114
|
-
property,
|
|
115
|
-
value: newValue,
|
|
116
|
-
locale: currentLocale.value,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
</script>
|
|
121
|
-
```
|
|
89
|
+
- **Empty values are cleaned up automatically** via `cleanEmptyValues()`. When user clears a field, scaffolding objects are removed and `property.values` becomes `[]`. This prevents false modification detection in `useBladeForm`.
|
|
90
|
+
- **Multilanguage cleanup**: Only the value for the current locale is removed; other locales are preserved.
|
|
91
|
+
- **Dictionary**: Expands `localizedValues` into per-locale value entries.
|
|
122
92
|
|
|
123
|
-
|
|
93
|
+
### Empty Value Cleanup (cleanEmptyValues)
|
|
124
94
|
|
|
125
|
-
|
|
126
|
-
<script setup lang="ts">
|
|
127
|
-
import { useDynamicProperties } from '@vc-shell/framework';
|
|
128
|
-
import { ref } from 'vue';
|
|
95
|
+
When `setPropertyValue` receives an empty value (`""`, `null`, `undefined`), it cleans up `property.values` instead of leaving scaffolding objects:
|
|
129
96
|
|
|
130
|
-
|
|
97
|
+
```
|
|
98
|
+
Before: values: [{ value: "", languageCode: "en", propertyId: "abc", ... }]
|
|
99
|
+
After: values: []
|
|
100
|
+
```
|
|
131
101
|
|
|
132
|
-
|
|
102
|
+
This ensures that `useBladeForm`'s deep comparison correctly detects "no change" when user clears a field that was originally empty.
|
|
133
103
|
|
|
134
|
-
|
|
135
|
-
const items = await loadDictionaries(property.id!, keyword, locale);
|
|
136
|
-
dictOptions.value = items ?? [];
|
|
137
|
-
}
|
|
138
|
-
</script>
|
|
104
|
+
## Recipe: With VcDynamicProperty
|
|
139
105
|
|
|
106
|
+
```vue
|
|
140
107
|
<template>
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
108
|
+
<VcDynamicProperty
|
|
109
|
+
v-for="property in properties"
|
|
110
|
+
:key="property.id"
|
|
111
|
+
:property="property"
|
|
112
|
+
:model-value="getPropertyValue(property, currentLocale)"
|
|
113
|
+
:options-getter="loadDictionaries"
|
|
114
|
+
:measurements-getter="loadMeasurements"
|
|
115
|
+
:current-language="currentLocale"
|
|
116
|
+
:value-type="property.valueType ?? ''"
|
|
117
|
+
:dictionary="property.dictionary"
|
|
118
|
+
:multivalue="property.multivalue"
|
|
119
|
+
:multilanguage="property.multilanguage"
|
|
120
|
+
@update:model-value="(ev) => setPropertyValue({ property, ...ev })"
|
|
147
121
|
/>
|
|
148
122
|
</template>
|
|
123
|
+
|
|
124
|
+
<script setup lang="ts">
|
|
125
|
+
import { useDynamicProperties } from "@vc-shell/framework";
|
|
126
|
+
|
|
127
|
+
const { getPropertyValue, setPropertyValue, loadDictionaries, loadMeasurements } =
|
|
128
|
+
useDynamicProperties({
|
|
129
|
+
searchDictionary: searchDictionaryItems,
|
|
130
|
+
searchMeasurements: searchMeasurementItems,
|
|
131
|
+
});
|
|
132
|
+
</script>
|
|
149
133
|
```
|
|
150
134
|
|
|
151
135
|
## Tips
|
|
152
136
|
|
|
153
|
-
- **`setPropertyValue` mutates the property in place.**
|
|
154
|
-
- **Always pass `dictionary` when setting dictionary values.** Without
|
|
155
|
-
- **
|
|
156
|
-
- **
|
|
157
|
-
|
|
137
|
+
- **`setPropertyValue` mutates the property in place.** `property.values` is modified directly. This is by design because dynamic properties are typically part of a larger entity object saved as a whole.
|
|
138
|
+
- **Always pass `dictionary` when setting dictionary values.** Without dictionary items, the composable cannot resolve `valueId` to the correct alias and localized values.
|
|
139
|
+
- **Boolean properties always have a value entry.** Unlike other types where clearing removes the value, boolean properties maintain a value entry (`value: false`). This ensures checkboxes render correctly.
|
|
140
|
+
- **No class constructors needed.** Values are created as plain objects via `createValue()`. No factory classes required.
|
|
141
|
+
|
|
142
|
+
## File Structure
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
useDynamicProperties/
|
|
146
|
+
index.ts — composable entry point (~60 lines)
|
|
147
|
+
types.ts — all interfaces
|
|
148
|
+
utils.ts — isEmptyValue, createValue, cleanEmptyValues, type guards
|
|
149
|
+
strategies/
|
|
150
|
+
index.ts — resolveStrategy() registry
|
|
151
|
+
types.ts — PropertyValueStrategy interface
|
|
152
|
+
regular.ts — ShortText, LongText, Number, Integer, DateTime
|
|
153
|
+
boolean.ts — Boolean
|
|
154
|
+
dictionary.ts — Dictionary (all multi/single × language/value)
|
|
155
|
+
measure.ts — Measure
|
|
156
|
+
color.ts — Color
|
|
157
|
+
```
|
|
158
158
|
|
|
159
159
|
## Related
|
|
160
160
|
|
|
161
|
-
- [
|
|
162
|
-
-
|
|
163
|
-
- `PropertyValue`, `PropertyDictionaryItem` from `@core/api/platform` -- concrete types for the standard platform
|
|
161
|
+
- [useBladeForm](../useBladeForm/useBladeForm.docs.md) — form state management that uses `semanticEqual` for modification detection. `cleanEmptyValues` ensures compatibility.
|
|
162
|
+
- [VcDynamicProperty](../../../../ui/components/organisms/vc-dynamic-property/vc-dynamic-property.docs.md) — UI component that renders dynamic properties
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# usePopup
|
|
2
|
+
|
|
3
|
+
Programmatic popup management for modal dialogs. Returns methods to open confirmation, error, and info dialogs, as well as fully custom popup components with typed props and emits. Popups are rendered in a dedicated container at the app root level, ensuring they overlay all other content including blades and sidebars.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
- Show confirmation dialogs before destructive actions (delete, discard, bulk operations)
|
|
8
|
+
- Display error or info messages in a modal overlay
|
|
9
|
+
- Open custom popup components with typed props and emits
|
|
10
|
+
- When NOT to use: for passive feedback (use `notification()` toast instead), for navigation flows (use blade navigation instead)
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```vue
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
import { usePopup } from "@vc-shell/framework";
|
|
17
|
+
|
|
18
|
+
const { showConfirmation, showError } = usePopup();
|
|
19
|
+
|
|
20
|
+
async function deleteProduct(id: string) {
|
|
21
|
+
const confirmed = await showConfirmation(
|
|
22
|
+
"Are you sure you want to delete this product?"
|
|
23
|
+
);
|
|
24
|
+
if (!confirmed) return;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await api.deleteProduct(id);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
showError(error.message || "Failed to delete product.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<VcBlade title="Product">
|
|
36
|
+
<VcButton variant="danger" @click="deleteProduct(product.id)">
|
|
37
|
+
Delete
|
|
38
|
+
</VcButton>
|
|
39
|
+
</VcBlade>
|
|
40
|
+
</template>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### Parameters
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Required | Description |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `options` | `MaybeRef<UsePopupProps<T>>` | No | Configuration for a custom popup component. Omit for built-in dialogs only. |
|
|
50
|
+
|
|
51
|
+
#### `UsePopupProps<T>`
|
|
52
|
+
|
|
53
|
+
| Field | Type | Required | Description |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| `component` | `ComponentPublicInstanceConstructor` | Yes | The popup component to render |
|
|
56
|
+
| `props` | `RawProps<T>` | No | Props to pass to the component (typed from the component's `defineProps`) |
|
|
57
|
+
| `emits` | `RawEmits<T>` | No | Event handlers (typed from the component's `defineEmits`) |
|
|
58
|
+
| `slots` | `Record<string, string \| Component \| Slot>` | No | Named slots — strings render as text, components render as VNodes |
|
|
59
|
+
|
|
60
|
+
### Returns (`IUsePopup`)
|
|
61
|
+
|
|
62
|
+
| Method | Signature | Description |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `open` | `() => void` | Push the popup onto the stack and render it |
|
|
65
|
+
| `close` | `() => void` | Remove the popup from the stack |
|
|
66
|
+
| `showConfirmation` | `(message: string \| Ref<string>) => Promise<boolean>` | Warning dialog with Confirm/Cancel buttons. Resolves `true` on confirm, `false` on cancel or close. |
|
|
67
|
+
| `showError` | `(message: string \| Ref<string>) => void` | Error-styled popup with a close button |
|
|
68
|
+
| `showInfo` | `(message: string \| Ref<string>) => void` | Info-styled popup with a close button |
|
|
69
|
+
|
|
70
|
+
## How It Works
|
|
71
|
+
|
|
72
|
+
`usePopup` is backed by the `VcPopupHandler` plugin, which maintains a reactive array of popup instances. When you call `open()` or `showConfirmation()`, a popup descriptor is pushed into this array. The `VcPopupContainer` component (mounted once at the app root) renders all active popups as an overlay stack. Calling `close()` removes the descriptor, and Vue reactivity handles the unmount.
|
|
73
|
+
|
|
74
|
+
The plugin instance is resolved via `inject(PopupPluginKey)`. If called outside the component tree (e.g., in a utility function), it falls back to the singleton `popupPluginInstance`.
|
|
75
|
+
|
|
76
|
+
Multiple popups can be open simultaneously — they stack with the most recent on top.
|
|
77
|
+
|
|
78
|
+
## Recipe: Delete Confirmation with Notification
|
|
79
|
+
|
|
80
|
+
```vue
|
|
81
|
+
<script setup lang="ts">
|
|
82
|
+
import { usePopup, useBlade } from "@vc-shell/framework";
|
|
83
|
+
import { useNotifications } from "@vc-shell/framework";
|
|
84
|
+
|
|
85
|
+
const { showConfirmation, showError } = usePopup();
|
|
86
|
+
const { closeSelf } = useBlade();
|
|
87
|
+
|
|
88
|
+
async function deleteOrder(id: string) {
|
|
89
|
+
const confirmed = await showConfirmation(
|
|
90
|
+
"Are you sure you want to delete this order? This action cannot be undone."
|
|
91
|
+
);
|
|
92
|
+
if (!confirmed) return;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await api.deleteOrder(id);
|
|
96
|
+
closeSelf();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
showError(error.message || "Failed to delete order.");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
</script>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Recipe: Custom Popup with Form
|
|
105
|
+
|
|
106
|
+
For complex interactions beyond simple confirmation, create a custom popup component:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { usePopup } from "@vc-shell/framework";
|
|
110
|
+
import AddNotePopup from "./AddNotePopup.vue";
|
|
111
|
+
|
|
112
|
+
const { open, close } = usePopup({
|
|
113
|
+
component: AddNotePopup,
|
|
114
|
+
props: { entityId: "prod-1" },
|
|
115
|
+
emits: {
|
|
116
|
+
onConfirm: async (note: string) => {
|
|
117
|
+
await api.addNote("prod-1", note);
|
|
118
|
+
close();
|
|
119
|
+
},
|
|
120
|
+
onCancel: () => close(),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
open();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Recipe: Reactive Options
|
|
128
|
+
|
|
129
|
+
The `options` parameter accepts `MaybeRef`, so you can pass a computed or ref that updates the popup config dynamically:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { computed } from "vue";
|
|
133
|
+
import { usePopup } from "@vc-shell/framework";
|
|
134
|
+
import ConfirmPopup from "./ConfirmPopup.vue";
|
|
135
|
+
|
|
136
|
+
const selectedCount = ref(0);
|
|
137
|
+
|
|
138
|
+
const { open, close } = usePopup(
|
|
139
|
+
computed(() => ({
|
|
140
|
+
component: ConfirmPopup,
|
|
141
|
+
props: { message: `Delete ${selectedCount.value} items?` },
|
|
142
|
+
emits: { onConfirm: () => { performDelete(); close(); } },
|
|
143
|
+
}))
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Common Mistakes
|
|
148
|
+
|
|
149
|
+
**Wrong: not awaiting showConfirmation**
|
|
150
|
+
```ts
|
|
151
|
+
// The delete runs immediately — confirmation is ignored
|
|
152
|
+
showConfirmation("Delete?");
|
|
153
|
+
await api.deleteProduct(id); // runs before user clicks!
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Correct: await the result**
|
|
157
|
+
```ts
|
|
158
|
+
const confirmed = await showConfirmation("Delete?");
|
|
159
|
+
if (!confirmed) return;
|
|
160
|
+
await api.deleteProduct(id);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
**Wrong: nesting many popups**
|
|
166
|
+
```ts
|
|
167
|
+
// 3+ deep popup stacks confuse users
|
|
168
|
+
const a = await showConfirmation("Step 1?");
|
|
169
|
+
if (a) {
|
|
170
|
+
const b = await showConfirmation("Step 2?");
|
|
171
|
+
if (b) {
|
|
172
|
+
const c = await showConfirmation("Step 3?");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Correct: use a blade for multi-step workflows**
|
|
178
|
+
```ts
|
|
179
|
+
// Complex flows belong in blades, not popups
|
|
180
|
+
openBlade({ name: "WizardBlade", options: { step: 1 } });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Tips
|
|
184
|
+
|
|
185
|
+
- **Always `await` showConfirmation** before proceeding with the destructive action.
|
|
186
|
+
- **Use `showConfirmation` for yes/no questions**, custom popups for forms or complex interactions.
|
|
187
|
+
- **Calling `usePopup()` without arguments** gives you only the built-in dialogs (`showConfirmation`, `showError`, `showInfo`). Pass `options` only when you need a custom component.
|
|
188
|
+
- **Avoid nesting popups more than 2 levels deep.** Consider blades for complex multi-step workflows.
|
|
189
|
+
- **The message parameter accepts `Ref<string>`** — useful for dynamic messages that update while the popup is open (e.g., progress messages).
|
|
190
|
+
|
|
191
|
+
## Related
|
|
192
|
+
|
|
193
|
+
- [VcPopup](../../../ui/components/organisms/vc-popup/vc-popup.docs.md) — the default popup shell component (header, content, actions)
|
|
194
|
+
- [useBlade](../useBlade/useBlade.docs.md) — for panel-based UI; use popups for modal interruptions only
|
|
195
|
+
- `notification()` — for passive, non-blocking feedback (toasts)
|