Add M3 text fields, selects and selection controls
tests / feature (8.4) (push) Successful in 1m5s
tests / feature (8.5) (push) Successful in 1m4s
tests / lint (push) Successful in 59s
tests / browser (chrome, chromium) (push) Successful in 2m54s
tests / browser (firefox, firefox) (push) Successful in 3m3s
tests / browser (safari, webkit) (push) Successful in 4m13s
tests / feature (8.4) (push) Successful in 1m5s
tests / feature (8.5) (push) Successful in 1m4s
tests / lint (push) Successful in 59s
tests / browser (chrome, chromium) (push) Successful in 2m54s
tests / browser (firefox, firefox) (push) Successful in 3m3s
tests / browser (safari, webkit) (push) Successful in 4m13s
Outlined and filled text fields (input, password, auto-growing textarea, native select with the customizable select menu, file), checkbox with an indeterminate state, radio buttons, the M3 switch and form, ported from ReStride and extended. The first half of Phase 6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
ad724662ca
commit
7f92f1cb29
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* What the form controls need from script, which is little: both are kept off Alpine so they work
|
||||
* in any markup and before Alpine starts.
|
||||
*
|
||||
* `<x-textarea>` grows with its text. CSS does that where the browser can size a field to its
|
||||
* content (`field-sizing: content`, components/field.css); elsewhere the height is set here from
|
||||
* the scroll height, on every input and whenever a Livewire render or navigation puts a new value
|
||||
* in.
|
||||
*
|
||||
* `<x-checkbox indeterminate>`: HTML has no attribute for a checkbox's mixed state, only the
|
||||
* `indeterminate` property, so the component marks the input `data-indeterminate` and this keeps
|
||||
* the property in step with the mark — when the page loads, and when a render adds or removes the
|
||||
* mark. A person pressing the box clears the property as the browser always does; the mark stays
|
||||
* until the server decides again.
|
||||
*/
|
||||
const GROWING = 'textarea[data-autogrow]'
|
||||
const MIXED = 'input[type="checkbox"][data-indeterminate]'
|
||||
|
||||
const growsByItself = window.CSS?.supports?.('field-sizing', 'content') ?? false
|
||||
|
||||
function grow(textarea) {
|
||||
textarea.style.height = 'auto'
|
||||
textarea.style.height = `${textarea.scrollHeight + textarea.offsetHeight - textarea.clientHeight}px`
|
||||
}
|
||||
|
||||
function growAll(root) {
|
||||
if (! growsByItself) {
|
||||
root.querySelectorAll(GROWING).forEach(grow)
|
||||
}
|
||||
}
|
||||
|
||||
function markAll(root) {
|
||||
root.querySelectorAll(MIXED).forEach((input) => (input.indeterminate = true))
|
||||
}
|
||||
|
||||
if (! growsByItself) {
|
||||
document.addEventListener('input', (event) => {
|
||||
if (event.target instanceof HTMLTextAreaElement && event.target.matches(GROWING)) {
|
||||
grow(event.target)
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener('livewire:init', () => {
|
||||
// A render that only puts a new value in a textarea changes no attribute or node.
|
||||
window.Livewire.hook('morphed', ({ el }) => growAll(el))
|
||||
})
|
||||
}
|
||||
|
||||
new MutationObserver((records) => {
|
||||
for (const record of records) {
|
||||
if (record.type === 'attributes') {
|
||||
if (record.attributeName === 'data-indeterminate' && record.target instanceof HTMLInputElement) {
|
||||
record.target.indeterminate = record.target.hasAttribute('data-indeterminate')
|
||||
} else if (! growsByItself && record.target.matches(GROWING)) {
|
||||
grow(record.target)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for (const node of record.addedNodes) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const input of [node, ...node.querySelectorAll(MIXED)].filter((element) => element.matches(MIXED))) {
|
||||
input.indeterminate = true
|
||||
}
|
||||
|
||||
if (! growsByItself) {
|
||||
;[node, ...node.querySelectorAll(GROWING)].filter((element) => element.matches(GROWING)).forEach(grow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).observe(document.documentElement, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['data-indeterminate', 'data-autogrow'],
|
||||
})
|
||||
|
||||
markAll(document)
|
||||
growAll(document)
|
||||
@@ -17,3 +17,4 @@ import './progress.js'
|
||||
import './list-rows.js'
|
||||
import './bottom-sheet.js'
|
||||
import './carousel.js'
|
||||
import './field.js'
|
||||
|
||||
Reference in New Issue
Block a user