-
Notifications
You must be signed in to change notification settings - Fork 13.5k
feat(input): add workaround for dynamic slot content #27636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
5f83994
feat(input): add base label slot implementation
liamdebeasi 514b677
test(input): update tests
liamdebeasi de1f128
chore(): add updated snapshots
Ionitron 1f867f2
test(input): add spec and a11y tests
liamdebeasi 5618a55
note feature is experimental
liamdebeasi a343c37
feat: add bae slot mutation observer
liamdebeasi a905a91
add base slot mutation observer impl
liamdebeasi 03d4032
clean up code
liamdebeasi f0da048
add test
liamdebeasi b66a627
chore(): add updated snapshots
Ionitron d36c413
sync
liamdebeasi 126ae3e
update test for outline notch
liamdebeasi d48a950
listen for changes to slotted content itself
liamdebeasi b02d1cc
add correct usage check
liamdebeasi b66bd8d
update file location
liamdebeasi cea8a22
lint
liamdebeasi 7d2ea9d
fix: notch cutout is hidden with no label
liamdebeasi db1d32e
fix: re-render when slotted content is removed
liamdebeasi 455a35b
add test
liamdebeasi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+6.54 KB
...acement/input.e2e.ts-snapshots/input-async-label-md-ltr-Mobile-Chrome-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+2.37 KB
...cement/input.e2e.ts-snapshots/input-async-label-md-ltr-Mobile-Firefox-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+6.24 KB
...acement/input.e2e.ts-snapshots/input-async-label-md-ltr-Mobile-Safari-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,8 +91,49 @@ <h2>Outline / Floating</h2> | |
<div slot="label">Email <span class="required">*</span></div> | ||
</ion-input> | ||
</div> | ||
|
||
<div class="grid-item"> | ||
<h2>Outline / Floating / Async</h2> | ||
<ion-input id="solid-async" label-placement="floating" fill="outline" value="[email protected]"></ion-input> | ||
</div> | ||
</div> | ||
|
||
<ion-button onclick="addSlot()">Add Slotted Content</ion-button> | ||
<ion-button onclick="updateSlot()">Update Slotted Content</ion-button> | ||
<ion-button onclick="removeSlot()">Remove Slotted Content</ion-button> | ||
</ion-content> | ||
</ion-app> | ||
|
||
<script> | ||
const solidAsync = document.querySelector('#solid-async'); | ||
|
||
const getSlottedContent = () => { | ||
return solidAsync.querySelector('[slot="label"]'); | ||
}; | ||
|
||
const addSlot = () => { | ||
if (getSlottedContent() === null) { | ||
const labelEl = document.createElement('div'); | ||
labelEl.slot = 'label'; | ||
labelEl.innerHTML = 'Email <span class="required">*</span>'; | ||
|
||
solidAsync.appendChild(labelEl); | ||
} | ||
}; | ||
|
||
const removeSlot = () => { | ||
if (getSlottedContent() !== null) { | ||
solidAsync.querySelector('[slot="label"]').remove(); | ||
} | ||
}; | ||
|
||
const updateSlot = () => { | ||
const slottedContent = getSlottedContent(); | ||
|
||
if (slottedContent !== null) { | ||
slottedContent.textContent = 'This is my really really really long text'; | ||
} | ||
}; | ||
</script> | ||
</body> | ||
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { win } from '@utils/browser'; | ||
import { raf } from '@utils/helpers'; | ||
/** | ||
* Used to update a scoped component that uses emulated slots. This fires when | ||
* content is passed into the slot or when the content inside of a slot changes. | ||
* This is not needed for components using native slots in the Shadow DOM. | ||
* @internal | ||
* @param el The host element to observe | ||
* @param slotName mutationCallback will fire when nodes on this slot change | ||
* @param mutationCallback The callback to fire whenever the slotted content changes | ||
*/ | ||
export const createSlotMutationController = ( | ||
el: HTMLElement, | ||
slotName: string, | ||
mutationCallback: () => void | ||
): SlotMutationController => { | ||
let hostMutationObserver: MutationObserver | undefined; | ||
let slottedContentMutationObserver: MutationObserver | undefined; | ||
|
||
if (win !== undefined && 'MutationObserver' in win) { | ||
hostMutationObserver = new MutationObserver((entries) => { | ||
for (const entry of entries) { | ||
for (const node of entry.addedNodes) { | ||
/** | ||
* Check to see if the added node | ||
* is our slotted content. | ||
*/ | ||
if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).slot === slotName) { | ||
/** | ||
* If so, we want to watch the slotted | ||
* content itself for changes. This lets us | ||
* detect when content inside of the slot changes. | ||
*/ | ||
mutationCallback(); | ||
|
||
/** | ||
* Adding the listener in an raf | ||
* waits until Stencil moves the slotted element | ||
* into the correct place in the event that | ||
* slotted content is being added. | ||
*/ | ||
raf(() => watchForSlotChange(node as HTMLElement)); | ||
return; | ||
} | ||
} | ||
} | ||
}); | ||
|
||
hostMutationObserver.observe(el, { | ||
childList: true, | ||
}); | ||
} | ||
|
||
/** | ||
* Listen for changes inside of the slotted content. | ||
* We can listen for subtree changes here to be | ||
* informed of text within the slotted content | ||
* changing. Doing this on the host is possible | ||
* but it is much more expensive to do because | ||
* it also listens for changes to the internals | ||
* of the component. | ||
*/ | ||
const watchForSlotChange = (slottedEl: HTMLElement) => { | ||
if (slottedContentMutationObserver) { | ||
slottedContentMutationObserver.disconnect(); | ||
slottedContentMutationObserver = undefined; | ||
} | ||
|
||
slottedContentMutationObserver = new MutationObserver((entries) => { | ||
mutationCallback(); | ||
|
||
for (const entry of entries) { | ||
for (const node of entry.removedNodes) { | ||
/** | ||
* If the element was removed then we | ||
* need to destroy the MutationObserver | ||
* so the element can be garbage collected. | ||
*/ | ||
if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).slot === slotName) { | ||
destroySlottedContentObserver(); | ||
} | ||
} | ||
} | ||
}); | ||
|
||
/** | ||
* Listen for changes inside of the element | ||
* as well as anything deep in the tree. | ||
* We listen on the parentElement so that we can | ||
* detect when slotted element itself is removed. | ||
*/ | ||
slottedContentMutationObserver.observe(slottedEl.parentElement ?? slottedEl, { subtree: true, childList: true }); | ||
}; | ||
|
||
const destroy = () => { | ||
if (hostMutationObserver) { | ||
hostMutationObserver.disconnect(); | ||
hostMutationObserver = undefined; | ||
} | ||
|
||
destroySlottedContentObserver(); | ||
}; | ||
|
||
const destroySlottedContentObserver = () => { | ||
if (slottedContentMutationObserver) { | ||
slottedContentMutationObserver.disconnect(); | ||
slottedContentMutationObserver = undefined; | ||
} | ||
}; | ||
|
||
return { | ||
destroy, | ||
}; | ||
}; | ||
|
||
export type SlotMutationController = { | ||
destroy: () => void; | ||
}; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.