From f002cb6398732e065155d749389525b9376f5de2 Mon Sep 17 00:00:00 2001 From: arnoson Date: Thu, 29 Jun 2023 20:39:38 +0200 Subject: [PATCH] feat: add deep refs --- README.md | 15 +++++++++++++++ index.html | 8 ++++++-- src/refs.ts | 21 +++++++++++++++++---- tests/index.test.ts | 24 ++++++++++++++++-------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2cfa364..3d2adf6 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,21 @@ registerComponent('parent', options, ({ refs }) => { ``` +### Deep/Nested Refs + +Sometimes you may want to associate a ref that is nested inside another component with the parent component instead. You can do so by providing a path with the parent components name: `parent/ref`. + +```html +
+
+ + +
+
+``` + +Referencing the parent component by it's name as above is the most common scenario, but in some rare cases you may want to target a specific html element. You can do this by using a CSS selector surrounded by parentheses: `(#my-component)/ref` or `(div.some-class)/ref`. + ### Events Components try to stay as close to native APIs as possible. Therefore events are just [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent), but they can be fully typed: diff --git a/index.html b/index.html index 03fd755..01e2ff0 100644 --- a/index.html +++ b/index.html @@ -7,9 +7,13 @@ Document -
+
0 - +
+
+ +
+
diff --git a/src/refs.ts b/src/refs.ts index b3bef9b..3f56c00 100644 --- a/src/refs.ts +++ b/src/refs.ts @@ -3,12 +3,25 @@ import { walkComponent } from './walkComponent' export const getRefs = (el: HTMLElement) => { const refsAll: SimpleRefsAll = {} + const addRef = (name: string, el: HTMLElement) => { + refsAll[name] ??= [] + refsAll[name].push(el) + } + walkComponent(el, el => { const { ref } = el.dataset - if (ref) { - refsAll[ref] ??= [] - refsAll[ref].push(el) - } + if (ref) addRef(ref, el) + }) + + const deepRefs = el.querySelectorAll('[data-ref*="/"]') + deepRefs.forEach(refEl => { + const [parent, name] = refEl.dataset.ref!.split('/') + + const selector = parent.match(/^\((.*)\)$/)?.[1] + const parentSelector = selector ?? `[data-simple-component='${parent}']` + + const parentEl = el.closest(parentSelector) + if (parentEl === el) addRef(name, refEl) }) const refs: SimpleRefs = Object.fromEntries( diff --git a/tests/index.test.ts b/tests/index.test.ts index be735d8..8831c1e 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -73,18 +73,22 @@ it('provides a record of single refs', () => { registerComponent('test', component) document.body.innerHTML = ` -
-
+
+
- -
+
+
+
` mountComponents(document.body) - const myRef = document.querySelector(`[data-ref='myRef']`) - expect(component).toBeCalledWith(expect.objectContaining({ refs: { myRef } })) + const myRef = document.querySelector('#ref1') + const deepRef = document.querySelector('#ref2') + const selectorDeepRef = document.querySelector('#ref3') + expect(component).toBeCalledWith( + expect.objectContaining({ refs: { myRef, deepRef, selectorDeepRef } }) + ) }) it('provides a record of groups of refs with the same name', () => { @@ -96,13 +100,17 @@ it('provides a record of groups of refs with the same name', () => {
+
+
+
` mountComponents(document.body) const myRef = [ document.querySelector('#ref1'), document.querySelector('#ref2'), - document.querySelector('#ref3') + document.querySelector('#ref3'), + document.querySelector('#ref4') ] expect(component).toBeCalledWith( expect.objectContaining({ refsAll: { myRef } })