@vc-shell/vc-app-skill 2.0.0-alpha.31 → 2.0.0-alpha.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +1 -1
  3. package/runtime/VERSION +1 -1
  4. package/runtime/agents/migration-agent.md +83 -0
  5. package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
  6. package/runtime/knowledge/docs/core/composables/bladeContext/index.docs.md +111 -0
  7. package/runtime/knowledge/docs/core/composables/useBladeForm/useBladeForm.docs.md +167 -0
  8. package/runtime/knowledge/docs/core/composables/useBladeWidgets/index.docs.md +305 -0
  9. package/runtime/knowledge/docs/core/composables/useMenuExpanded/index.docs.md +83 -0
  10. package/runtime/knowledge/docs/core/utilities/thumbnail/thumbnail.docs.md +116 -0
  11. package/runtime/knowledge/docs/shell/components/change-password-button/change-password-button.docs.md +1 -1
  12. package/runtime/knowledge/docs/ui/components/atoms/vc-card/vc-card.docs.md +4 -0
  13. package/runtime/knowledge/docs/ui/components/molecules/vc-accordion/vc-accordion.docs.md +4 -0
  14. package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox/vc-checkbox.docs.md +5 -0
  15. package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox-group/vc-checkbox-group.docs.md +5 -0
  16. package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +5 -0
  17. package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +7 -0
  18. package/runtime/knowledge/docs/ui/components/molecules/vc-editor/vc-editor.docs.md +5 -0
  19. package/runtime/knowledge/docs/ui/components/molecules/vc-field/vc-field.docs.md +5 -0
  20. package/runtime/knowledge/docs/ui/components/molecules/vc-file-upload/vc-file-upload.docs.md +5 -0
  21. package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +7 -0
  22. package/runtime/knowledge/docs/ui/components/molecules/vc-input-currency/vc-input-currency.docs.md +7 -0
  23. package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +7 -0
  24. package/runtime/knowledge/docs/ui/components/molecules/vc-multivalue/vc-multivalue.docs.md +7 -0
  25. package/runtime/knowledge/docs/ui/components/molecules/vc-radio-button/vc-radio-button.docs.md +5 -0
  26. package/runtime/knowledge/docs/ui/components/molecules/vc-radio-group/vc-radio-group.docs.md +5 -0
  27. package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +5 -0
  28. package/runtime/knowledge/docs/ui/components/molecules/vc-select/vc-select.docs.md +7 -0
  29. package/runtime/knowledge/docs/ui/components/molecules/vc-slider/vc-slider.docs.md +5 -0
  30. package/runtime/knowledge/docs/ui/components/molecules/vc-switch/vc-switch.docs.md +5 -0
  31. package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +7 -0
  32. package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +30 -0
  33. package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +28 -0
  34. package/runtime/knowledge/migration-prompts/blade-form-migration.md +246 -0
  35. package/runtime/knowledge/migration-prompts/blade-props-migration.md +195 -0
  36. package/runtime/knowledge/migration-prompts/notifications-migration.md +218 -0
  37. package/runtime/knowledge/migration-prompts/nswag-migration.md +248 -0
  38. package/runtime/knowledge/migration-prompts/widgets-migration.md +157 -0
  39. package/runtime/vc-app.md +126 -0
  40. package/runtime/knowledge/docs/core/constants/constants.docs.md +0 -185
  41. /package/runtime/knowledge/docs/shell/{pages → auth}/ChangePasswordPage/change-password-page.docs.md +0 -0
@@ -144,3 +144,8 @@ interface RadioGroupOption<V = string | number | boolean> {
144
144
  - [VcRadioButton](../vc-radio-button/) — individual radio button component
145
145
  - [VcCheckboxGroup](../vc-checkbox-group/) — multiple-selection option group
146
146
  - [VcInputGroup](../vc-input-group/) — generic form field group (used internally)
147
+
148
+ ## Skeleton / Loading State
149
+
150
+ When placed inside a `VcBlade` with `loading=true`, the component renders a skeleton placeholder matching its shape — a control indicator and label block. No configuration needed.
151
+
@@ -251,3 +251,8 @@ Use the `"star-and-text"` or `"text"` variant in table cell slots for a compact
251
251
  - [VcField](../vc-field/) -- Read-only field display, often used alongside ratings in detail views
252
252
  - [VcLabel](../../atoms/vc-label/) -- Label atom used internally for the label and tooltip
253
253
  - [VcIcon](../../atoms/vc-icon/) -- Renders the star icons internally
254
+
255
+ ## Skeleton / Loading State
256
+
257
+ When placed inside a `VcBlade` with `loading=true`, the component automatically renders a skeleton placeholder matching its visual footprint. No additional props or configuration needed.
258
+
@@ -682,3 +682,10 @@ Don't forget `:key` with cascading selects, otherwise the second select will ret
682
682
  - [VcInput](../vc-input/) — simple text field
683
683
  - [VcDatePicker](../vc-date-picker/) — date selection
684
684
  - [VcField](../vc-field/) — wrapper with label/error/hint (read-only display)
685
+
686
+ ## Skeleton / Loading State
687
+
688
+ When placed inside a `VcBlade` with `loading=true`, the component automatically renders a skeleton placeholder matching its visual footprint — a label block (when the `label` prop is set) and an input-shaped block. No additional props or configuration needed.
689
+
690
+ This behavior is powered by `BladeLoadingKey` via Vue's provide/inject. The component injects the loading state from the nearest `VcBlade` ancestor.
691
+
@@ -113,3 +113,8 @@ const products = [
113
113
  ## Related Components
114
114
 
115
115
  - [VcImage](../../atoms/vc-image/) -- image component often used inside slides
116
+
117
+ ## Skeleton / Loading State
118
+
119
+ When placed inside a `VcBlade` with `loading=true`, the component automatically renders a skeleton placeholder matching its visual footprint. No additional props or configuration needed.
120
+
@@ -298,3 +298,8 @@ const isActive = ref(true);
298
298
  - [VcCheckbox](../vc-checkbox/) -- for checkboxes and checkbox groups
299
299
  - [VcRadioButton](../vc-radio-button/) -- for mutually exclusive choices
300
300
  - [VcInputGroup](../vc-input-group/) -- semantic wrapper for grouping form controls
301
+
302
+ ## Skeleton / Loading State
303
+
304
+ When placed inside a `VcBlade` with `loading=true`, the component renders a skeleton placeholder matching its shape — a control indicator and label block. No configuration needed.
305
+
@@ -305,3 +305,10 @@ const description = ref<string>("");
305
305
  - [VcInput](../vc-input/) -- single-line text input
306
306
  - [VcEditor](../vc-editor/) -- rich text / Markdown editor
307
307
  - [VcInputGroup](../vc-input-group/) -- semantic wrapper for grouping form controls
308
+
309
+ ## Skeleton / Loading State
310
+
311
+ When placed inside a `VcBlade` with `loading=true`, the component automatically renders a skeleton placeholder matching its visual footprint — a label block (when the `label` prop is set) and an input-shaped block. No additional props or configuration needed.
312
+
313
+ This behavior is powered by `BladeLoadingKey` via Vue's provide/inject. The component injects the loading state from the nearest `VcBlade` ancestor.
314
+
@@ -659,3 +659,33 @@ openBlade({ name: "ProductsList" });
659
659
  | `useLoading()` | Merges multiple loading refs into a single boolean. |
660
660
  | `usePopup()` | Confirmation dialogs and error messages. |
661
661
  | `useBladeWidgets()` | Register contextual widgets for the blade widget area. |
662
+
663
+ ## Content Skeleton Mode
664
+
665
+ When `loading=true`, VcBlade provides `BladeLoadingKey` to all descendant components via Vue's provide/inject. Each framework UI component automatically renders a skeleton placeholder matching its visual footprint.
666
+
667
+ ### How It Works
668
+
669
+ 1. Set `loading` prop on VcBlade (typically bound to your data-fetching composable's loading ref)
670
+ 2. All child components (`VcInput`, `VcSelect`, `VcCard`, etc.) detect the loading state and render skeletons
671
+ 3. Layout containers (`VcContainer`, `VcRow`, `VcCol`, `VcForm`) are transparent — they preserve layout structure
672
+ 4. When loading completes, components switch to their normal rendering
673
+
674
+ ### What Shows During Loading
675
+
676
+ - **Config-gated fields** (`v-if="config.showName"`) — config is available immediately, so these fields show skeletons
677
+ - **Data-gated fields** (`v-if="item.productData"`) — data not yet loaded, so these fields don't appear in the skeleton
678
+ - **Header and toolbar** — have their own dedicated skeletons (unchanged)
679
+
680
+ No changes to existing blade pages are required.
681
+
682
+ ### Custom Components
683
+
684
+ To make custom components skeleton-aware:
685
+
686
+ ```ts
687
+ import { useBladeLoading } from "@vc-shell/framework";
688
+
689
+ const bladeLoading = useBladeLoading();
690
+ // bladeLoading.value === true when parent VcBlade is loading
691
+ ```
@@ -619,6 +619,33 @@ Customize expand/collapse icons:
619
619
  </VcDataTable>
620
620
  ```
621
621
 
622
+ ### Conditional expansion
623
+
624
+ Use `isRowExpandable` to control which rows show the expand toggle. Rows that fail the predicate cannot be expanded:
625
+
626
+ ```vue
627
+ <template>
628
+ <VcDataTable
629
+ :items="orders"
630
+ v-model:expanded-rows="expandedRows"
631
+ :is-row-expandable="(order) => order.lineItems.length > 0"
632
+ >
633
+ <VcColumn id="expand" :expander="true" :width="40" />
634
+ <VcColumn id="orderNumber" field="number" title="Order #" />
635
+
636
+ <template #expansion="{ data }">
637
+ <div class="tw-p-4">
638
+ <p v-for="item in data.lineItems" :key="item.id">{{ item.name }}</p>
639
+ </div>
640
+ </template>
641
+ </VcDataTable>
642
+ </template>
643
+
644
+ <script setup lang="ts">
645
+ const expandedRows = ref<Order[]>([]);
646
+ </script>
647
+ ```
648
+
622
649
  ---
623
650
 
624
651
  ## Row Grouping
@@ -1264,6 +1291,7 @@ function onRowRemove(event: { data: Product; index: number; cancel: () => void }
1264
1291
  | `expandedRows` | `T[]` | `[]` | Expanded rows. Use with `v-model:expandedRows`. |
1265
1292
  | `expandedRowIcon` | `string` | `"lucide-chevron-down"` | Icon for expanded state. |
1266
1293
  | `collapsedRowIcon` | `string` | `"lucide-chevron-right"` | Icon for collapsed state. |
1294
+ | `isRowExpandable` | `(data: T) => boolean` | -- | Per-row predicate to hide the expand toggle. |
1267
1295
 
1268
1296
  ### Row Grouping
1269
1297
 
@@ -0,0 +1,246 @@
1
+ ---
2
+ name: blade-form-migration
3
+ description: AI transformation rules for useForm + onBeforeClose → useBladeForm.
4
+ ---
5
+
6
+ # Blade Form Migration: useForm → useBladeForm
7
+
8
+ Replace manual wiring of `useForm()` (vee-validate) + `useModificationTracker()` + `useBeforeUnload()` + `onBeforeClose()` with the unified `useBladeForm()` composable.
9
+
10
+ ## RULE 1: Replace useForm + onBeforeClose with useBladeForm
11
+
12
+ **BEFORE:**
13
+
14
+ ```typescript
15
+ import { useForm } from "vee-validate";
16
+ import { useBeforeUnload, useModificationTracker, useBlade, usePopup } from "@vc-shell/framework";
17
+
18
+ const { item, loading, load, save } = useMyDetails();
19
+ const { onBeforeClose } = useBlade();
20
+ const { showConfirmation } = usePopup();
21
+
22
+ const { errorBag, setFieldError, meta: formMeta } = useForm({
23
+ initialValues: item,
24
+ });
25
+
26
+ const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
27
+
28
+ useBeforeUnload(computed(() => isModified.value));
29
+
30
+ onBeforeClose(async () => {
31
+ if (isModified.value) {
32
+ return showConfirmation(t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"));
33
+ }
34
+ return true;
35
+ });
36
+
37
+ const canSave = computed(
38
+ () => isModified.value && formMeta.value.valid && !loading.value,
39
+ );
40
+
41
+ async function handleSave() {
42
+ await save(item.value);
43
+ resetModificationState();
44
+ }
45
+
46
+ async function handleCancel() {
47
+ item.value = JSON.parse(JSON.stringify(currentValue.value));
48
+ resetModificationState();
49
+ }
50
+ ```
51
+
52
+ **AFTER:**
53
+
54
+ ```typescript
55
+ import { useBladeForm } from "@vc-shell/framework";
56
+
57
+ const { item, loading, load, save } = useMyDetails();
58
+
59
+ const { canSave, isModified, setBaseline, revert, setFieldError, errorBag } = useBladeForm({
60
+ data: item,
61
+ closeConfirmMessage: () => t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"),
62
+ });
63
+
64
+ watch(item, () => {
65
+ if (item.value) setBaseline();
66
+ }, { once: true });
67
+
68
+ async function handleSave() {
69
+ await save(item.value);
70
+ setBaseline();
71
+ }
72
+ ```
73
+
74
+ ## RULE 2: Remove onBeforeClose Entirely
75
+
76
+ `useBladeForm` handles the unsaved-changes guard automatically via `autoBeforeClose` (defaults to `true`).
77
+
78
+ **BEFORE:**
79
+
80
+ ```typescript
81
+ const { onBeforeClose } = useBlade();
82
+
83
+ onBeforeClose(async () => {
84
+ if (formMeta.value.dirty) {
85
+ return confirm("You have unsaved changes. Discard?");
86
+ }
87
+ return true;
88
+ });
89
+ ```
90
+
91
+ **AFTER:**
92
+
93
+ ```typescript
94
+ // Remove entirely. useBladeForm handles this.
95
+ // If you need a custom message, pass closeConfirmMessage option:
96
+ const form = useBladeForm({
97
+ data: item,
98
+ closeConfirmMessage: () => t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"),
99
+ });
100
+ ```
101
+
102
+ ## RULE 3: Update Toolbar Disabled Logic
103
+
104
+ **BEFORE:**
105
+
106
+ ```typescript
107
+ const canSave = computed(
108
+ () => !meta.value.valid || !isModified.value,
109
+ );
110
+
111
+ // or in toolbar items:
112
+ {
113
+ id: "save",
114
+ disabled: computed(() => !formMeta.value.valid || !isModified.value),
115
+ }
116
+ ```
117
+
118
+ **AFTER:**
119
+
120
+ ```typescript
121
+ // canSave is returned directly from useBladeForm:
122
+ const { canSave } = useBladeForm({ data: item });
123
+
124
+ // In toolbar items:
125
+ {
126
+ id: "save",
127
+ disabled: computed(() => !canSave.value),
128
+ }
129
+ ```
130
+
131
+ For additional disabled conditions (e.g., async validation in progress), use `canSaveOverride`:
132
+
133
+ ```typescript
134
+ const { canSave } = useBladeForm({
135
+ data: item,
136
+ canSaveOverride: computed(() => !isSkuValidating.value && !loading.value),
137
+ });
138
+ ```
139
+
140
+ ## RULE 4: Keep Field from vee-validate
141
+
142
+ Only remove `useForm` from vee-validate imports. Keep `Field`, `ErrorMessage`, and other template-level components — they still work with `useBladeForm` which uses vee-validate internally.
143
+
144
+ **BEFORE:**
145
+
146
+ ```typescript
147
+ import { useForm, Field, ErrorMessage } from "vee-validate";
148
+ ```
149
+
150
+ **AFTER:**
151
+
152
+ ```typescript
153
+ import { Field, ErrorMessage } from "vee-validate";
154
+ ```
155
+
156
+ ## RULE 5: Remove useBeforeUnload / useModificationTracker
157
+
158
+ `useBladeForm` handles both browser unload guards and modification tracking internally.
159
+
160
+ **BEFORE:**
161
+
162
+ ```typescript
163
+ import { useBeforeUnload, useModificationTracker } from "@vc-shell/framework";
164
+
165
+ const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
166
+ useBeforeUnload(computed(() => isModified.value));
167
+ ```
168
+
169
+ **AFTER:**
170
+
171
+ ```typescript
172
+ // Remove entirely. useBladeForm provides:
173
+ // - isModified (deep comparison against baseline)
174
+ // - setBaseline() (replaces resetModificationState)
175
+ // - revert() (replaces manual JSON.parse(JSON.stringify(currentValue.value)))
176
+ // - Browser unload guard (automatic)
177
+ ```
178
+
179
+ Also remove `useModificationTracker` from data composables if present:
180
+
181
+ **BEFORE (data composable):**
182
+
183
+ ```typescript
184
+ export function useMyDetails() {
185
+ const item = ref<MyItem>();
186
+ const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
187
+
188
+ async function load(id: string) {
189
+ item.value = await api.get(id);
190
+ resetModificationState();
191
+ }
192
+
193
+ return { item, currentValue, resetModificationState, isModified, load, save };
194
+ }
195
+ ```
196
+
197
+ **AFTER (data composable):**
198
+
199
+ ```typescript
200
+ export function useMyDetails() {
201
+ const item = ref<MyItem>();
202
+
203
+ async function load(id: string) {
204
+ item.value = await api.get(id);
205
+ }
206
+
207
+ return { item, load, save };
208
+ }
209
+ ```
210
+
211
+ ## Template Changes
212
+
213
+ Remove `:modified` prop from `<VcBlade>` — it is auto-detected via provide/inject.
214
+
215
+ **BEFORE:**
216
+
217
+ ```vue
218
+ <template>
219
+ <VcBlade :modified="isModified" :closable="true">
220
+ <!-- content -->
221
+ </VcBlade>
222
+ </template>
223
+ ```
224
+
225
+ **AFTER:**
226
+
227
+ ```vue
228
+ <template>
229
+ <VcBlade :closable="true">
230
+ <!-- content — no :modified prop needed -->
231
+ </VcBlade>
232
+ </template>
233
+ ```
234
+
235
+ ## Verification
236
+
237
+ After migration:
238
+
239
+ 1. Run `npx tsc --noEmit` to verify no TypeScript errors
240
+ 2. Confirm the blade shows a modified indicator when data changes
241
+ 3. Confirm closing a modified blade shows the confirmation dialog
242
+ 4. Confirm the save button is disabled when form is clean or invalid
243
+ 5. Confirm `setBaseline()` is called after initial load and after save
244
+ 6. Confirm cancel/revert restores data to the last baseline
245
+ 7. Confirm `useModificationTracker` is removed from data composables
246
+ 8. Confirm no stale imports of `useForm`, `useBeforeUnload`, `useModificationTracker`, or `onBeforeClose`
@@ -0,0 +1,195 @@
1
+ ---
2
+ name: blade-props-migration
3
+ description: AI transformation rules for reusable blade components skipped by CLI migrator.
4
+ ---
5
+
6
+ # Blade Props Migration: Boilerplate Removal
7
+
8
+ Remove blade prop/emit boilerplate from blade pages. VcBlade is now context-aware — it reads `expanded` and `closable` from the BladeDescriptor automatically via provide/inject. Use `useBlade()` for `param`, `options`, `closeSelf`, and `callParent`.
9
+
10
+ ## RULE 1: Remove Blade Prop Forwarding from Template
11
+
12
+ Remove `:expanded`, `:closable`, `@close`, `@expand`, `@collapse` from the `<VcBlade>` tag.
13
+
14
+ **BEFORE:**
15
+
16
+ ```vue
17
+ <template>
18
+ <VcBlade
19
+ :title="bladeTitle"
20
+ :expanded="expanded"
21
+ :closable="closable"
22
+ :toolbar-items="bladeToolbar"
23
+ @close="$emit('close:blade')"
24
+ @expand="$emit('expand:blade')"
25
+ @collapse="$emit('collapse:blade')"
26
+ >
27
+ <VcDataTable ... />
28
+ </VcBlade>
29
+ </template>
30
+ ```
31
+
32
+ **AFTER:**
33
+
34
+ ```vue
35
+ <template>
36
+ <VcBlade
37
+ :title="bladeTitle"
38
+ :toolbar-items="bladeToolbar"
39
+ >
40
+ <VcDataTable ... />
41
+ </VcBlade>
42
+ </template>
43
+ ```
44
+
45
+ ## RULE 2: Remove Props and Emits Interfaces
46
+
47
+ Remove `expanded`, `closable`, `param`, `options` from Props interface. Remove blade events from Emits. Use `useBlade()` to access `param` and `options`.
48
+
49
+ **BEFORE:**
50
+
51
+ ```typescript
52
+ export interface Props {
53
+ expanded?: boolean;
54
+ closable?: boolean;
55
+ param?: string;
56
+ options?: { sellerProduct?: SellerProduct };
57
+ }
58
+
59
+ export interface Emits {
60
+ (event: "parent:call", args: IParentCallArgs): void;
61
+ (event: "close:blade"): void;
62
+ (event: "collapse:blade"): void;
63
+ (event: "expand:blade"): void;
64
+ }
65
+
66
+ const props = withDefaults(defineProps<Props>(), {
67
+ expanded: true,
68
+ closable: true,
69
+ });
70
+ const emit = defineEmits<Emits>();
71
+ ```
72
+
73
+ **AFTER:**
74
+
75
+ ```typescript
76
+ import { useBlade } from "@vc-shell/framework";
77
+
78
+ const { param, options, openBlade, closeSelf, callParent } =
79
+ useBlade<{ sellerProduct?: SellerProduct }>();
80
+ ```
81
+
82
+ If the Props interface has other non-blade props, keep only those:
83
+
84
+ ```typescript
85
+ // Keep non-blade props
86
+ export interface Props {
87
+ config?: DetailConfig;
88
+ }
89
+
90
+ const props = defineProps<Props>();
91
+
92
+ // Blade concerns via composable
93
+ const { param, options, closeSelf, callParent } = useBlade<{ orderId: string }>();
94
+ ```
95
+
96
+ ## RULE 3: Replace emit("parent:call") with callParent()
97
+
98
+ **BEFORE:**
99
+
100
+ ```typescript
101
+ const emit = defineEmits<Emits>();
102
+
103
+ const reload = async () => {
104
+ await loadOffers({ ... });
105
+ emit("parent:call", { method: "reload" });
106
+ };
107
+
108
+ function onItemSelect(item: Product) {
109
+ emit("parent:call", { method: "onItemClick", args: { data: item } });
110
+ }
111
+ ```
112
+
113
+ **AFTER:**
114
+
115
+ ```typescript
116
+ const { callParent } = useBlade();
117
+
118
+ const reload = async () => {
119
+ await loadOffers({ ... });
120
+ callParent("reload");
121
+ };
122
+
123
+ function onItemSelect(item: Product) {
124
+ callParent("onItemClick", { data: item });
125
+ }
126
+ ```
127
+
128
+ ## RULE 4: Replace emit("close:blade") with closeSelf()
129
+
130
+ **BEFORE:**
131
+
132
+ ```typescript
133
+ const emit = defineEmits<Emits>();
134
+
135
+ function handleClose() {
136
+ emit("close:blade");
137
+ }
138
+
139
+ // or in template:
140
+ // @click="$emit('close:blade')"
141
+ ```
142
+
143
+ **AFTER:**
144
+
145
+ ```typescript
146
+ const { closeSelf } = useBlade();
147
+
148
+ function handleClose() {
149
+ closeSelf();
150
+ }
151
+
152
+ // or in template:
153
+ // @click="closeSelf()"
154
+ ```
155
+
156
+ ## Wrapper Components
157
+
158
+ If a wrapper component forwards blade props to a base component, remove the forwarding. The base component should call `useBlade()` directly.
159
+
160
+ **BEFORE:**
161
+
162
+ ```vue
163
+ <template>
164
+ <ProductDetailsBase
165
+ :expanded="expanded"
166
+ :closable="closable"
167
+ :param="param"
168
+ :options="options"
169
+ @parent:call="$emit('parent:call', $event)"
170
+ @close:blade="$emit('close:blade')"
171
+ @expand:blade="$emit('expand:blade')"
172
+ @collapse:blade="$emit('collapse:blade')"
173
+ />
174
+ </template>
175
+ ```
176
+
177
+ **AFTER:**
178
+
179
+ ```vue
180
+ <template>
181
+ <ProductDetailsBase />
182
+ </template>
183
+ ```
184
+
185
+ ## Verification
186
+
187
+ After migration:
188
+
189
+ 1. Run `npx tsc --noEmit` to verify no TypeScript errors
190
+ 2. Confirm blades open and close correctly
191
+ 3. Confirm `param.value` is accessible in script (use `param` without `.value` in templates)
192
+ 4. Confirm `options.value` provides typed options from parent
193
+ 5. Confirm `callParent()` triggers the parent's exposed method
194
+ 6. Confirm no remaining `emit("close:blade")` or `emit("parent:call", ...)` calls
195
+ 7. Confirm no remaining `:expanded`, `:closable`, `@close`, `@expand`, `@collapse` on `<VcBlade>`