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 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
+ ```
@@ -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
+ }