vue-unwrap 1.0.0
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/README.md +106 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +44 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# vue-unwrap
|
|
2
|
+
|
|
3
|
+
Less wrappers, more control!
|
|
4
|
+
|
|
5
|
+
## What/why
|
|
6
|
+
|
|
7
|
+
When building reusable Vue components, it is common to encapsulate logic that needs access to the rendered DOM of its children — whether to observe layout changes, measure elements, coordinate animations/interactions, or perform low-level DOM operations.
|
|
8
|
+
|
|
9
|
+
In many cases, this creates an awkward tradeoff: either manually coordinate refs across component boundaries, or introduce an extra wrapper element purely as an implementation detail.
|
|
10
|
+
|
|
11
|
+
Neither option is ideal.
|
|
12
|
+
|
|
13
|
+
__Unwrap__ is a conditional wrapper component for Vue 3 with child ref aggregation that removes that friction by making child DOM access predictable while keeping wrapper elements optional. Components can stay focused on behavior rather than structure, avoiding unnecessary DOM nodes unless they are actually required.
|
|
14
|
+
|
|
15
|
+
The result is cleaner markup, simpler reusable abstractions, and more control over how components render.
|
|
16
|
+
|
|
17
|
+
## Getting started
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i vue-unwrap@latest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Basic usage
|
|
24
|
+
|
|
25
|
+
### With Unwrap component
|
|
26
|
+
|
|
27
|
+
```vue
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { computed, useTemplateRef } from 'vue'
|
|
30
|
+
import { getNodeElement, Unwrap } from 'vue-unwrap'
|
|
31
|
+
|
|
32
|
+
const ctx = useTemplateRef<typeof Unwrap>('ctx')
|
|
33
|
+
|
|
34
|
+
const $children = computed(() => ctx.value?.children.map(getNodeElement))
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<Unwrap ref="ctx" />
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### With useUnwrap composable (for more control)
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import type { ComponentPublicInstance } from 'vue'
|
|
48
|
+
import { useUnwrap } from 'vue-unwrap'
|
|
49
|
+
|
|
50
|
+
// your component props definition
|
|
51
|
+
type PropsType = { foo: string }
|
|
52
|
+
|
|
53
|
+
// your child nodes definition
|
|
54
|
+
type CustomNodeType = (
|
|
55
|
+
| ComponentPublicInstance<{ bar: string }>
|
|
56
|
+
| ComponentPublicInstance
|
|
57
|
+
| Element
|
|
58
|
+
| null
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
const props = defineProps<PropsType>()
|
|
62
|
+
|
|
63
|
+
const { $el, children, Unwrap } = useUnwrap<CustomNodeType, PropsType>()
|
|
64
|
+
|
|
65
|
+
const bars = computed(() => (
|
|
66
|
+
children?
|
|
67
|
+
.map(child => (
|
|
68
|
+
(child && 'bar' in child)
|
|
69
|
+
? `${props.foo}-${child.bar}`
|
|
70
|
+
: undefined
|
|
71
|
+
))
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
))
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<Unwrap is="div" />
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
- Install dependencies:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm install
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- Run the playground:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run playground
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Run the unit tests:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm run test
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- Build the library:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm run build
|
|
106
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as _$vue from "vue";
|
|
2
|
+
import { ComponentPublicInstance, SetupContext } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/useUnwrap.d.ts
|
|
5
|
+
type NodeRef = Element | ComponentPublicInstance | null;
|
|
6
|
+
declare function getNodeElement<N = NodeRef>(node: N): Element | null;
|
|
7
|
+
declare function useUnwrap<N = NodeRef, P = Record<string, any>>(propTypes?: Record<string, any>): {
|
|
8
|
+
$el: _$vue.ShallowRef<Element | null, Element | null>;
|
|
9
|
+
children: _$vue.ShallowReactive<N[]>;
|
|
10
|
+
Unwrap: {
|
|
11
|
+
(props: P, context: SetupContext): _$vue.VNode<_$vue.RendererNode, _$vue.RendererElement, {
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}> | _$vue.VNode<_$vue.RendererNode, _$vue.RendererElement, {
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}>[];
|
|
16
|
+
inheritAttrs: boolean;
|
|
17
|
+
props: {
|
|
18
|
+
[x: string]: any;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/Unwrap.vue.d.ts
|
|
24
|
+
declare const __VLS_export: _$vue.DefineComponent<{}, {
|
|
25
|
+
$el: _$vue.ShallowRef<Element | null, Element | null>;
|
|
26
|
+
children: _$vue.ShallowReactive<NodeRef[]>;
|
|
27
|
+
}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, _$vue.ComponentProvideOptions, true, {}, any>;
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { NodeRef, _default as Unwrap, getNodeElement, useUnwrap };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cloneVNode, createBlock, defineComponent, h, openBlock, shallowReactive, shallowRef, unref, useSlots } from "vue";
|
|
2
|
+
//#region src/useUnwrap.ts
|
|
3
|
+
function getNodeElement(node) {
|
|
4
|
+
const el = node?.$el || node;
|
|
5
|
+
return el instanceof Element ? el : null;
|
|
6
|
+
}
|
|
7
|
+
function useUnwrap(propTypes) {
|
|
8
|
+
const $el = shallowRef(null);
|
|
9
|
+
const children = shallowReactive([]);
|
|
10
|
+
const slots = useSlots();
|
|
11
|
+
const trackChildRef = (index) => (node) => children[index] = node;
|
|
12
|
+
const trackWrapperRef = (node) => $el.value = getNodeElement(node);
|
|
13
|
+
function Unwrap(props, context) {
|
|
14
|
+
const { is, ...attrs } = context.attrs;
|
|
15
|
+
const nodes = (slots.default?.(props) ?? []).map((node, i) => cloneVNode(node, { ref: trackChildRef(i) }, true));
|
|
16
|
+
const wrapper = is ? cloneVNode(h(is, attrs, nodes), { ref: trackWrapperRef }, true) : null;
|
|
17
|
+
children.splice(nodes.length);
|
|
18
|
+
return wrapper || nodes;
|
|
19
|
+
}
|
|
20
|
+
Unwrap.inheritAttrs = false;
|
|
21
|
+
Unwrap.props = { ...propTypes };
|
|
22
|
+
return {
|
|
23
|
+
$el,
|
|
24
|
+
children,
|
|
25
|
+
Unwrap
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/Unwrap.vue
|
|
30
|
+
var Unwrap_default = /* @__PURE__ */ defineComponent({
|
|
31
|
+
__name: "Unwrap",
|
|
32
|
+
setup(__props, { expose: __expose }) {
|
|
33
|
+
const { $el, children, Unwrap } = useUnwrap();
|
|
34
|
+
__expose({
|
|
35
|
+
$el,
|
|
36
|
+
children
|
|
37
|
+
});
|
|
38
|
+
return (_ctx, _cache) => {
|
|
39
|
+
return openBlock(), createBlock(unref(Unwrap));
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { Unwrap_default as Unwrap, getNodeElement, useUnwrap };
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vue-unwrap",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Conditional wrapper component for Vue 3 with child ref aggregation.",
|
|
6
|
+
"author": "Andrea 'Fiad' Fiadone <hello@fiad.one>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/fiadone/vue-unwrap#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/fiadone/vue-unwrap.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/fiadone/vue-unwrap/issues"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown",
|
|
28
|
+
"dev": "tsdown --watch",
|
|
29
|
+
"play": "vite",
|
|
30
|
+
"test": "vitest",
|
|
31
|
+
"typecheck": "vue-tsc --noEmit",
|
|
32
|
+
"release": "bumpp",
|
|
33
|
+
"prepublishOnly": "npm run build"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vue": "^3.5.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
40
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
41
|
+
"@semantic-release/git": "^10.0.1",
|
|
42
|
+
"@semantic-release/npm": "^13.1.5",
|
|
43
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
44
|
+
"@tsdown/css": "^0.22.0",
|
|
45
|
+
"@types/node": "^25.6.2",
|
|
46
|
+
"@vitejs/plugin-vue": "^6.0.6",
|
|
47
|
+
"@vitest/browser-playwright": "^4.1.5",
|
|
48
|
+
"bumpp": "^11.1.0",
|
|
49
|
+
"commitizen": "^4.3.1",
|
|
50
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
51
|
+
"playwright": "^1.59.1",
|
|
52
|
+
"semantic-release": "^25.0.3",
|
|
53
|
+
"tsdown": "^0.22.0",
|
|
54
|
+
"typescript": "^6.0.3",
|
|
55
|
+
"unrun": "^0.2.39",
|
|
56
|
+
"vite": "^8.0.11",
|
|
57
|
+
"vitest": "^4.1.5",
|
|
58
|
+
"vitest-browser-vue": "^2.1.0",
|
|
59
|
+
"vue": "^3.5.34",
|
|
60
|
+
"vue-tsc": "^3.2.8"
|
|
61
|
+
}
|
|
62
|
+
}
|