@sharedex/ui 0.9.0 → 0.10.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 +37 -2
- package/dist/backgrounds/hosts/CssHost.d.ts +2 -6
- package/dist/backgrounds/hosts/CssHost.js +3 -17
- package/dist/backgrounds/hosts/styleHref.d.ts +6 -0
- package/dist/backgrounds/hosts/styleHref.js +20 -0
- package/dist/buttons/pick.d.ts +9 -3
- package/dist/buttons/pick.js +4 -1
- package/dist/forms/base.d.ts +1 -0
- package/dist/forms/base.js +224 -0
- package/dist/forms/buddy.d.ts +6 -0
- package/dist/forms/buddy.js +150 -0
- package/dist/forms/chips.d.ts +1 -0
- package/dist/forms/chips.js +140 -0
- package/dist/forms/choice.d.ts +1 -0
- package/dist/forms/choice.js +208 -0
- package/dist/forms/controls/CheckGroup.d.ts +65 -0
- package/dist/forms/controls/CheckGroup.js +99 -0
- package/dist/forms/controls/Checkbox.d.ts +43 -0
- package/dist/forms/controls/Checkbox.js +67 -0
- package/dist/forms/controls/ChipGroup.d.ts +50 -0
- package/dist/forms/controls/ChipGroup.js +56 -0
- package/dist/forms/controls/Combobox.d.ts +75 -0
- package/dist/forms/controls/Combobox.js +249 -0
- package/dist/forms/controls/ErrorSummary.d.ts +29 -0
- package/dist/forms/controls/ErrorSummary.js +79 -0
- package/dist/forms/controls/Field.d.ts +92 -0
- package/dist/forms/controls/Field.js +219 -0
- package/dist/forms/controls/FileDrop.d.ts +81 -0
- package/dist/forms/controls/FileDrop.js +328 -0
- package/dist/forms/controls/FormBuddy.d.ts +22 -0
- package/dist/forms/controls/FormBuddy.js +143 -0
- package/dist/forms/controls/FormReveal.d.ts +21 -0
- package/dist/forms/controls/FormReveal.js +19 -0
- package/dist/forms/controls/FormSteps.d.ts +75 -0
- package/dist/forms/controls/FormSteps.js +205 -0
- package/dist/forms/controls/FormSwap.d.ts +27 -0
- package/dist/forms/controls/FormSwap.js +38 -0
- package/dist/forms/controls/InlineEdit.d.ts +50 -0
- package/dist/forms/controls/InlineEdit.js +136 -0
- package/dist/forms/controls/NumberField.d.ts +43 -0
- package/dist/forms/controls/NumberField.js +205 -0
- package/dist/forms/controls/OtpField.d.ts +46 -0
- package/dist/forms/controls/OtpField.js +195 -0
- package/dist/forms/controls/PasswordRules.d.ts +31 -0
- package/dist/forms/controls/PasswordRules.js +85 -0
- package/dist/forms/controls/RadioGroup.d.ts +69 -0
- package/dist/forms/controls/RadioGroup.js +95 -0
- package/dist/forms/controls/Range.d.ts +60 -0
- package/dist/forms/controls/Range.js +169 -0
- package/dist/forms/controls/SaveStatus.d.ts +25 -0
- package/dist/forms/controls/SaveStatus.js +88 -0
- package/dist/forms/controls/Select.d.ts +46 -0
- package/dist/forms/controls/Select.js +35 -0
- package/dist/forms/controls/Switch.d.ts +54 -0
- package/dist/forms/controls/Switch.js +91 -0
- package/dist/forms/controls/TagsField.d.ts +68 -0
- package/dist/forms/controls/TagsField.js +0 -0
- package/dist/forms/controls/choiceChecks.d.ts +11 -0
- package/dist/forms/controls/choiceChecks.js +19 -0
- package/dist/forms/controls/choiceParts.d.ts +40 -0
- package/dist/forms/controls/choiceParts.js +49 -0
- package/dist/forms/controls/fieldChecks.d.ts +17 -0
- package/dist/forms/controls/fieldChecks.js +34 -0
- package/dist/forms/controls/fieldTools.d.ts +62 -0
- package/dist/forms/controls/fieldTools.js +54 -0
- package/dist/forms/controls/pickerChecks.d.ts +10 -0
- package/dist/forms/controls/pickerChecks.js +27 -0
- package/dist/forms/controls/rangeChecks.d.ts +11 -0
- package/dist/forms/controls/rangeChecks.js +23 -0
- package/dist/forms/controls/shared.d.ts +8 -0
- package/dist/forms/controls/shared.js +18 -0
- package/dist/forms/effects/cards.d.ts +11 -0
- package/dist/forms/effects/cards.js +52 -0
- package/dist/forms/effects/check/checkCard.d.ts +10 -0
- package/dist/forms/effects/check/checkCard.js +15 -0
- package/dist/forms/effects/check/checkCard.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkCard.schema.js +9 -0
- package/dist/forms/effects/check/checkConfetti.d.ts +18 -0
- package/dist/forms/effects/check/checkConfetti.js +38 -0
- package/dist/forms/effects/check/checkConfetti.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkConfetti.schema.js +13 -0
- package/dist/forms/effects/check/checkDraw.d.ts +7 -0
- package/dist/forms/effects/check/checkDraw.js +12 -0
- package/dist/forms/effects/check/checkDraw.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkDraw.schema.js +9 -0
- package/dist/forms/effects/check/checkHeart.d.ts +9 -0
- package/dist/forms/effects/check/checkHeart.js +84 -0
- package/dist/forms/effects/check/checkHeart.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkHeart.schema.js +10 -0
- package/dist/forms/effects/check/checkJelly.d.ts +9 -0
- package/dist/forms/effects/check/checkJelly.js +23 -0
- package/dist/forms/effects/check/checkJelly.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkJelly.schema.js +10 -0
- package/dist/forms/effects/check/checkMerge.d.ts +9 -0
- package/dist/forms/effects/check/checkMerge.js +37 -0
- package/dist/forms/effects/check/checkMerge.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkMerge.schema.js +10 -0
- package/dist/forms/effects/check/checkMixed.d.ts +7 -0
- package/dist/forms/effects/check/checkMixed.js +25 -0
- package/dist/forms/effects/check/checkMixed.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkMixed.schema.js +9 -0
- package/dist/forms/effects/check/checkMorph.d.ts +9 -0
- package/dist/forms/effects/check/checkMorph.js +53 -0
- package/dist/forms/effects/check/checkMorph.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkMorph.schema.js +10 -0
- package/dist/forms/effects/check/checkPop.d.ts +9 -0
- package/dist/forms/effects/check/checkPop.js +27 -0
- package/dist/forms/effects/check/checkPop.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkPop.schema.js +10 -0
- package/dist/forms/effects/check/checkRipple.d.ts +7 -0
- package/dist/forms/effects/check/checkRipple.js +33 -0
- package/dist/forms/effects/check/checkRipple.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkRipple.schema.js +9 -0
- package/dist/forms/effects/check/checkScribble.d.ts +11 -0
- package/dist/forms/effects/check/checkScribble.js +42 -0
- package/dist/forms/effects/check/checkScribble.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkScribble.schema.js +11 -0
- package/dist/forms/effects/check/checkStrike.d.ts +9 -0
- package/dist/forms/effects/check/checkStrike.js +54 -0
- package/dist/forms/effects/check/checkStrike.schema.d.ts +1 -0
- package/dist/forms/effects/check/checkStrike.schema.js +10 -0
- package/dist/forms/effects/chip/chipCheck.d.ts +7 -0
- package/dist/forms/effects/chip/chipCheck.js +26 -0
- package/dist/forms/effects/chip/chipCheck.schema.d.ts +1 -0
- package/dist/forms/effects/chip/chipCheck.schema.js +9 -0
- package/dist/forms/effects/chip/chipMerge.d.ts +9 -0
- package/dist/forms/effects/chip/chipMerge.js +63 -0
- package/dist/forms/effects/chip/chipMerge.schema.d.ts +1 -0
- package/dist/forms/effects/chip/chipMerge.schema.js +10 -0
- package/dist/forms/effects/file/fileButton.d.ts +7 -0
- package/dist/forms/effects/file/fileButton.js +28 -0
- package/dist/forms/effects/file/fileButton.schema.d.ts +1 -0
- package/dist/forms/effects/file/fileButton.schema.js +9 -0
- package/dist/forms/effects/file/fileCards.d.ts +7 -0
- package/dist/forms/effects/file/fileCards.js +38 -0
- package/dist/forms/effects/file/fileCards.schema.d.ts +1 -0
- package/dist/forms/effects/file/fileCards.schema.js +9 -0
- package/dist/forms/effects/file/fileDrag.d.ts +9 -0
- package/dist/forms/effects/file/fileDrag.js +45 -0
- package/dist/forms/effects/file/fileDrag.schema.d.ts +1 -0
- package/dist/forms/effects/file/fileDrag.schema.js +10 -0
- package/dist/forms/effects/file/filePage.d.ts +7 -0
- package/dist/forms/effects/file/filePage.js +46 -0
- package/dist/forms/effects/file/filePage.schema.d.ts +1 -0
- package/dist/forms/effects/file/filePage.schema.js +9 -0
- package/dist/forms/effects/file/filePreview.d.ts +7 -0
- package/dist/forms/effects/file/filePreview.js +17 -0
- package/dist/forms/effects/file/filePreview.schema.d.ts +1 -0
- package/dist/forms/effects/file/filePreview.schema.js +9 -0
- package/dist/forms/effects/file/fileProgress.d.ts +9 -0
- package/dist/forms/effects/file/fileProgress.js +92 -0
- package/dist/forms/effects/file/fileProgress.schema.d.ts +1 -0
- package/dist/forms/effects/file/fileProgress.schema.js +10 -0
- package/dist/forms/effects/file/fileReject.d.ts +7 -0
- package/dist/forms/effects/file/fileReject.js +24 -0
- package/dist/forms/effects/file/fileReject.schema.d.ts +1 -0
- package/dist/forms/effects/file/fileReject.schema.js +9 -0
- package/dist/forms/effects/input/inputAside.d.ts +11 -0
- package/dist/forms/effects/input/inputAside.js +42 -0
- package/dist/forms/effects/input/inputAside.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputAside.schema.js +11 -0
- package/dist/forms/effects/input/inputBeam.d.ts +14 -0
- package/dist/forms/effects/input/inputBeam.js +51 -0
- package/dist/forms/effects/input/inputBeam.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputBeam.schema.js +18 -0
- package/dist/forms/effects/input/inputCaret.d.ts +13 -0
- package/dist/forms/effects/input/inputCaret.js +33 -0
- package/dist/forms/effects/input/inputCaret.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputCaret.schema.js +26 -0
- package/dist/forms/effects/input/inputCounter.d.ts +16 -0
- package/dist/forms/effects/input/inputCounter.js +84 -0
- package/dist/forms/effects/input/inputCounter.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputCounter.schema.js +12 -0
- package/dist/forms/effects/input/inputCycle.d.ts +19 -0
- package/dist/forms/effects/input/inputCycle.js +93 -0
- package/dist/forms/effects/input/inputCycle.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputCycle.schema.js +11 -0
- package/dist/forms/effects/input/inputDepth.d.ts +11 -0
- package/dist/forms/effects/input/inputDepth.js +31 -0
- package/dist/forms/effects/input/inputDepth.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputDepth.schema.js +11 -0
- package/dist/forms/effects/input/inputDraw.d.ts +13 -0
- package/dist/forms/effects/input/inputDraw.js +27 -0
- package/dist/forms/effects/input/inputDraw.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputDraw.schema.js +12 -0
- package/dist/forms/effects/input/inputFlap.d.ts +9 -0
- package/dist/forms/effects/input/inputFlap.js +24 -0
- package/dist/forms/effects/input/inputFlap.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputFlap.schema.js +10 -0
- package/dist/forms/effects/input/inputFloat.d.ts +12 -0
- package/dist/forms/effects/input/inputFloat.js +46 -0
- package/dist/forms/effects/input/inputFloat.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputFloat.schema.js +10 -0
- package/dist/forms/effects/input/inputFlood.d.ts +11 -0
- package/dist/forms/effects/input/inputFlood.js +30 -0
- package/dist/forms/effects/input/inputFlood.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputFlood.schema.js +11 -0
- package/dist/forms/effects/input/inputFrame.d.ts +18 -0
- package/dist/forms/effects/input/inputFrame.js +77 -0
- package/dist/forms/effects/input/inputFrame.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputFrame.schema.js +13 -0
- package/dist/forms/effects/input/inputIcon.d.ts +11 -0
- package/dist/forms/effects/input/inputIcon.js +18 -0
- package/dist/forms/effects/input/inputIcon.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputIcon.schema.js +11 -0
- package/dist/forms/effects/input/inputNotch.d.ts +9 -0
- package/dist/forms/effects/input/inputNotch.js +70 -0
- package/dist/forms/effects/input/inputNotch.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputNotch.schema.js +10 -0
- package/dist/forms/effects/input/inputReveal.d.ts +15 -0
- package/dist/forms/effects/input/inputReveal.js +56 -0
- package/dist/forms/effects/input/inputReveal.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputReveal.schema.js +16 -0
- package/dist/forms/effects/input/inputRing.d.ts +13 -0
- package/dist/forms/effects/input/inputRing.js +27 -0
- package/dist/forms/effects/input/inputRing.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputRing.schema.js +12 -0
- package/dist/forms/effects/input/inputRise.d.ts +9 -0
- package/dist/forms/effects/input/inputRise.js +22 -0
- package/dist/forms/effects/input/inputRise.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputRise.schema.js +10 -0
- package/dist/forms/effects/input/inputSearch.d.ts +9 -0
- package/dist/forms/effects/input/inputSearch.js +37 -0
- package/dist/forms/effects/input/inputSearch.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputSearch.schema.js +10 -0
- package/dist/forms/effects/input/inputSkeleton.d.ts +7 -0
- package/dist/forms/effects/input/inputSkeleton.js +41 -0
- package/dist/forms/effects/input/inputSkeleton.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputSkeleton.schema.js +9 -0
- package/dist/forms/effects/input/inputSpotlight.d.ts +9 -0
- package/dist/forms/effects/input/inputSpotlight.js +20 -0
- package/dist/forms/effects/input/inputSpotlight.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputSpotlight.schema.js +10 -0
- package/dist/forms/effects/input/inputStrength.d.ts +9 -0
- package/dist/forms/effects/input/inputStrength.js +29 -0
- package/dist/forms/effects/input/inputStrength.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputStrength.schema.js +10 -0
- package/dist/forms/effects/input/inputSwap.d.ts +11 -0
- package/dist/forms/effects/input/inputSwap.js +39 -0
- package/dist/forms/effects/input/inputSwap.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputSwap.schema.js +11 -0
- package/dist/forms/effects/input/inputUnderline.d.ts +13 -0
- package/dist/forms/effects/input/inputUnderline.js +36 -0
- package/dist/forms/effects/input/inputUnderline.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputUnderline.schema.js +18 -0
- package/dist/forms/effects/input/inputWave.d.ts +11 -0
- package/dist/forms/effects/input/inputWave.js +23 -0
- package/dist/forms/effects/input/inputWave.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputWave.schema.js +11 -0
- package/dist/forms/effects/input/inputWavy.d.ts +11 -0
- package/dist/forms/effects/input/inputWavy.js +53 -0
- package/dist/forms/effects/input/inputWavy.schema.d.ts +1 -0
- package/dist/forms/effects/input/inputWavy.schema.js +11 -0
- package/dist/forms/effects/kit.d.ts +213 -0
- package/dist/forms/effects/kit.js +226 -0
- package/dist/forms/effects/picker/chipPop.d.ts +7 -0
- package/dist/forms/effects/picker/chipPop.js +28 -0
- package/dist/forms/effects/picker/chipPop.schema.d.ts +1 -0
- package/dist/forms/effects/picker/chipPop.schema.js +9 -0
- package/dist/forms/effects/picker/listHighlight.d.ts +9 -0
- package/dist/forms/effects/picker/listHighlight.js +42 -0
- package/dist/forms/effects/picker/listHighlight.schema.d.ts +1 -0
- package/dist/forms/effects/picker/listHighlight.schema.js +10 -0
- package/dist/forms/effects/picker/listMatch.d.ts +7 -0
- package/dist/forms/effects/picker/listMatch.js +21 -0
- package/dist/forms/effects/picker/listMatch.schema.d.ts +1 -0
- package/dist/forms/effects/picker/listMatch.schema.js +9 -0
- package/dist/forms/effects/picker/pickerArrow.d.ts +9 -0
- package/dist/forms/effects/picker/pickerArrow.js +17 -0
- package/dist/forms/effects/picker/pickerArrow.schema.d.ts +1 -0
- package/dist/forms/effects/picker/pickerArrow.schema.js +10 -0
- package/dist/forms/effects/picker/pickerCheck.d.ts +7 -0
- package/dist/forms/effects/picker/pickerCheck.js +31 -0
- package/dist/forms/effects/picker/pickerCheck.schema.d.ts +1 -0
- package/dist/forms/effects/picker/pickerCheck.schema.js +9 -0
- package/dist/forms/effects/picker/pickerOpen.d.ts +12 -0
- package/dist/forms/effects/picker/pickerOpen.js +34 -0
- package/dist/forms/effects/picker/pickerOpen.schema.d.ts +1 -0
- package/dist/forms/effects/picker/pickerOpen.schema.js +16 -0
- package/dist/forms/effects/picker/pickerSheet.d.ts +9 -0
- package/dist/forms/effects/picker/pickerSheet.js +47 -0
- package/dist/forms/effects/picker/pickerSheet.schema.d.ts +1 -0
- package/dist/forms/effects/picker/pickerSheet.schema.js +10 -0
- package/dist/forms/effects/picker/pickerStagger.d.ts +9 -0
- package/dist/forms/effects/picker/pickerStagger.js +31 -0
- package/dist/forms/effects/picker/pickerStagger.schema.d.ts +1 -0
- package/dist/forms/effects/picker/pickerStagger.schema.js +10 -0
- package/dist/forms/effects/radio/radioCard.d.ts +10 -0
- package/dist/forms/effects/radio/radioCard.js +15 -0
- package/dist/forms/effects/radio/radioCard.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioCard.schema.js +9 -0
- package/dist/forms/effects/radio/radioCircle.d.ts +9 -0
- package/dist/forms/effects/radio/radioCircle.js +49 -0
- package/dist/forms/effects/radio/radioCircle.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioCircle.schema.js +10 -0
- package/dist/forms/effects/radio/radioDot.d.ts +7 -0
- package/dist/forms/effects/radio/radioDot.js +10 -0
- package/dist/forms/effects/radio/radioDot.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioDot.schema.js +9 -0
- package/dist/forms/effects/radio/radioEmoji.d.ts +9 -0
- package/dist/forms/effects/radio/radioEmoji.js +94 -0
- package/dist/forms/effects/radio/radioEmoji.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioEmoji.schema.js +10 -0
- package/dist/forms/effects/radio/radioPulse.d.ts +9 -0
- package/dist/forms/effects/radio/radioPulse.js +24 -0
- package/dist/forms/effects/radio/radioPulse.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioPulse.schema.js +10 -0
- package/dist/forms/effects/radio/radioScale.d.ts +9 -0
- package/dist/forms/effects/radio/radioScale.js +73 -0
- package/dist/forms/effects/radio/radioScale.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioScale.schema.js +10 -0
- package/dist/forms/effects/radio/radioSegment.d.ts +7 -0
- package/dist/forms/effects/radio/radioSegment.js +52 -0
- package/dist/forms/effects/radio/radioSegment.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioSegment.schema.js +9 -0
- package/dist/forms/effects/radio/radioStars.d.ts +15 -0
- package/dist/forms/effects/radio/radioStars.js +125 -0
- package/dist/forms/effects/radio/radioStars.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioStars.schema.js +13 -0
- package/dist/forms/effects/radio/radioTravel.d.ts +9 -0
- package/dist/forms/effects/radio/radioTravel.js +49 -0
- package/dist/forms/effects/radio/radioTravel.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioTravel.schema.js +10 -0
- package/dist/forms/effects/radio/radioUnderline.d.ts +9 -0
- package/dist/forms/effects/radio/radioUnderline.js +61 -0
- package/dist/forms/effects/radio/radioUnderline.schema.d.ts +1 -0
- package/dist/forms/effects/radio/radioUnderline.schema.js +10 -0
- package/dist/forms/effects/radio/shape.d.ts +13 -0
- package/dist/forms/effects/radio/shape.js +61 -0
- package/dist/forms/effects/range/rangeBubble.d.ts +9 -0
- package/dist/forms/effects/range/rangeBubble.js +54 -0
- package/dist/forms/effects/range/rangeBubble.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeBubble.schema.js +10 -0
- package/dist/forms/effects/range/rangeElastic.d.ts +9 -0
- package/dist/forms/effects/range/rangeElastic.js +101 -0
- package/dist/forms/effects/range/rangeElastic.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeElastic.schema.js +10 -0
- package/dist/forms/effects/range/rangeFace.d.ts +11 -0
- package/dist/forms/effects/range/rangeFace.js +68 -0
- package/dist/forms/effects/range/rangeFace.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeFace.schema.js +11 -0
- package/dist/forms/effects/range/rangeFill.d.ts +7 -0
- package/dist/forms/effects/range/rangeFill.js +29 -0
- package/dist/forms/effects/range/rangeFill.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeFill.schema.js +9 -0
- package/dist/forms/effects/range/rangeHistogram.d.ts +14 -0
- package/dist/forms/effects/range/rangeHistogram.js +65 -0
- package/dist/forms/effects/range/rangeHistogram.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeHistogram.schema.js +11 -0
- package/dist/forms/effects/range/rangeThumb.d.ts +9 -0
- package/dist/forms/effects/range/rangeThumb.js +39 -0
- package/dist/forms/effects/range/rangeThumb.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeThumb.schema.js +10 -0
- package/dist/forms/effects/range/rangeTicks.d.ts +9 -0
- package/dist/forms/effects/range/rangeTicks.js +44 -0
- package/dist/forms/effects/range/rangeTicks.schema.d.ts +1 -0
- package/dist/forms/effects/range/rangeTicks.schema.js +18 -0
- package/dist/forms/effects/ring.d.ts +5 -0
- package/dist/forms/effects/ring.js +12 -0
- package/dist/forms/effects/step/stepProgress.d.ts +9 -0
- package/dist/forms/effects/step/stepProgress.js +35 -0
- package/dist/forms/effects/step/stepProgress.schema.d.ts +1 -0
- package/dist/forms/effects/step/stepProgress.schema.js +10 -0
- package/dist/forms/effects/step/stepSlide.d.ts +9 -0
- package/dist/forms/effects/step/stepSlide.js +50 -0
- package/dist/forms/effects/step/stepSlide.schema.d.ts +1 -0
- package/dist/forms/effects/step/stepSlide.schema.js +10 -0
- package/dist/forms/effects/switch/switchBulb.d.ts +9 -0
- package/dist/forms/effects/switch/switchBulb.js +70 -0
- package/dist/forms/effects/switch/switchBulb.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchBulb.schema.js +10 -0
- package/dist/forms/effects/switch/switchDayNight.d.ts +15 -0
- package/dist/forms/effects/switch/switchDayNight.js +96 -0
- package/dist/forms/effects/switch/switchDayNight.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchDayNight.schema.js +13 -0
- package/dist/forms/effects/switch/switchFace.d.ts +7 -0
- package/dist/forms/effects/switch/switchFace.js +75 -0
- package/dist/forms/effects/switch/switchFace.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchFace.schema.js +9 -0
- package/dist/forms/effects/switch/switchFlip.d.ts +7 -0
- package/dist/forms/effects/switch/switchFlip.js +45 -0
- package/dist/forms/effects/switch/switchFlip.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchFlip.schema.js +9 -0
- package/dist/forms/effects/switch/switchGoo.d.ts +7 -0
- package/dist/forms/effects/switch/switchGoo.js +95 -0
- package/dist/forms/effects/switch/switchGoo.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchGoo.schema.js +9 -0
- package/dist/forms/effects/switch/switchIcon.d.ts +9 -0
- package/dist/forms/effects/switch/switchIcon.js +63 -0
- package/dist/forms/effects/switch/switchIcon.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchIcon.schema.js +10 -0
- package/dist/forms/effects/switch/switchLabels.d.ts +11 -0
- package/dist/forms/effects/switch/switchLabels.js +55 -0
- package/dist/forms/effects/switch/switchLabels.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchLabels.schema.js +11 -0
- package/dist/forms/effects/switch/switchMorph.d.ts +9 -0
- package/dist/forms/effects/switch/switchMorph.js +22 -0
- package/dist/forms/effects/switch/switchMorph.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchMorph.schema.js +10 -0
- package/dist/forms/effects/switch/switchNeon.d.ts +11 -0
- package/dist/forms/effects/switch/switchNeon.js +53 -0
- package/dist/forms/effects/switch/switchNeon.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchNeon.schema.js +11 -0
- package/dist/forms/effects/switch/switchSlide.d.ts +7 -0
- package/dist/forms/effects/switch/switchSlide.js +11 -0
- package/dist/forms/effects/switch/switchSlide.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchSlide.schema.js +9 -0
- package/dist/forms/effects/switch/switchStretch.d.ts +9 -0
- package/dist/forms/effects/switch/switchStretch.js +21 -0
- package/dist/forms/effects/switch/switchStretch.schema.d.ts +1 -0
- package/dist/forms/effects/switch/switchStretch.schema.js +10 -0
- package/dist/forms/effects/valid/validAsync.d.ts +7 -0
- package/dist/forms/effects/valid/validAsync.js +36 -0
- package/dist/forms/effects/valid/validAsync.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validAsync.schema.js +9 -0
- package/dist/forms/effects/valid/validBubble.d.ts +7 -0
- package/dist/forms/effects/valid/validBubble.js +61 -0
- package/dist/forms/effects/valid/validBubble.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validBubble.schema.js +9 -0
- package/dist/forms/effects/valid/validCheck.d.ts +9 -0
- package/dist/forms/effects/valid/validCheck.js +50 -0
- package/dist/forms/effects/valid/validCheck.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validCheck.schema.js +10 -0
- package/dist/forms/effects/valid/validColor.d.ts +11 -0
- package/dist/forms/effects/valid/validColor.js +28 -0
- package/dist/forms/effects/valid/validColor.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validColor.schema.js +11 -0
- package/dist/forms/effects/valid/validFlash.d.ts +9 -0
- package/dist/forms/effects/valid/validFlash.js +36 -0
- package/dist/forms/effects/valid/validFlash.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validFlash.schema.js +10 -0
- package/dist/forms/effects/valid/validMessage.d.ts +7 -0
- package/dist/forms/effects/valid/validMessage.js +24 -0
- package/dist/forms/effects/valid/validMessage.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validMessage.schema.js +9 -0
- package/dist/forms/effects/valid/validShake.d.ts +9 -0
- package/dist/forms/effects/valid/validShake.js +37 -0
- package/dist/forms/effects/valid/validShake.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validShake.schema.js +10 -0
- package/dist/forms/effects/valid/validSwap.d.ts +7 -0
- package/dist/forms/effects/valid/validSwap.js +33 -0
- package/dist/forms/effects/valid/validSwap.schema.d.ts +1 -0
- package/dist/forms/effects/valid/validSwap.schema.js +9 -0
- package/dist/forms/errors.d.ts +12 -0
- package/dist/forms/errors.js +20 -0
- package/dist/forms/file.d.ts +1 -0
- package/dist/forms/file.js +268 -0
- package/dist/forms/files.d.ts +7 -0
- package/dist/forms/files.js +29 -0
- package/dist/forms/formSteps.d.ts +9 -0
- package/dist/forms/formSteps.js +6 -0
- package/dist/forms/ids.d.ts +17 -0
- package/dist/forms/ids.js +13 -0
- package/dist/forms/inline.d.ts +3 -0
- package/dist/forms/inline.js +128 -0
- package/dist/forms/number.d.ts +1 -0
- package/dist/forms/number.js +67 -0
- package/dist/forms/otp.d.ts +4 -0
- package/dist/forms/otp.js +171 -0
- package/dist/forms/picker.d.ts +1 -0
- package/dist/forms/picker.js +303 -0
- package/dist/forms/range.d.ts +3 -0
- package/dist/forms/range.js +223 -0
- package/dist/forms/registry.d.ts +4 -0
- package/dist/forms/registry.js +133 -0
- package/dist/forms/reveal.d.ts +6 -0
- package/dist/forms/reveal.js +51 -0
- package/dist/forms/runtime/focus.d.ts +16 -0
- package/dist/forms/runtime/focus.js +55 -0
- package/dist/forms/runtime/listMatch.d.ts +2 -0
- package/dist/forms/runtime/listMatch.js +33 -0
- package/dist/forms/runtime/origin.d.ts +10 -0
- package/dist/forms/runtime/origin.js +25 -0
- package/dist/forms/runtime/placePopup.d.ts +10 -0
- package/dist/forms/runtime/placePopup.js +55 -0
- package/dist/forms/runtime/useFlip.d.ts +7 -0
- package/dist/forms/runtime/useFlip.js +38 -0
- package/dist/forms/runtime/useMorph.d.ts +8 -0
- package/dist/forms/runtime/useMorph.js +56 -0
- package/dist/forms/runtime/useOffScreen.d.ts +3 -0
- package/dist/forms/runtime/useOffScreen.js +16 -0
- package/dist/forms/runtime/useSwitchDrag.d.ts +8 -0
- package/dist/forms/runtime/useSwitchDrag.js +83 -0
- package/dist/forms/runtime/vanish.d.ts +6 -0
- package/dist/forms/runtime/vanish.js +185 -0
- package/dist/forms/steps.d.ts +1 -0
- package/dist/forms/steps.js +149 -0
- package/dist/forms/swap.d.ts +7 -0
- package/dist/forms/swap.js +149 -0
- package/dist/forms/switch.d.ts +1 -0
- package/dist/forms/switch.js +99 -0
- package/dist/forms/tools.d.ts +3 -0
- package/dist/forms/tools.js +248 -0
- package/dist/forms/types.d.ts +161 -0
- package/dist/forms/types.js +5 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +119 -0
- package/dist/schema/forms.d.ts +3 -0
- package/dist/schema/forms.js +194 -0
- package/dist/schema/index.d.ts +2 -1
- package/dist/schema/index.js +3 -1
- package/dist/schema/types.d.ts +2 -2
- package/dist/schema/types.js +1 -0
- package/guides/README.md +62 -61
- package/guides/contact.md +100 -77
- package/guides/forms.md +105 -0
- package/package.json +1 -1
- package/styles/form.css +594 -0
- package/theme.css +6 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { styleHref } from "../../backgrounds/hosts/CssHost.js";
|
|
5
|
+
import { motionOnly } from "../../transitions/progress.js";
|
|
6
|
+
import { BUDDY_CSS, BUDDY_MOTION } from "../buddy.js";
|
|
7
|
+
const TEXT_TYPES = new Set(["text", "email", "search", "url", "tel", "password", "number", ""]);
|
|
8
|
+
/** The text field that has the focus inside `scope` (or whose own tool button has it), if any. */
|
|
9
|
+
function focusedField(scope) {
|
|
10
|
+
const a = document.activeElement;
|
|
11
|
+
if (!a || !scope.contains(a))
|
|
12
|
+
return null;
|
|
13
|
+
if (a instanceof HTMLTextAreaElement)
|
|
14
|
+
return a;
|
|
15
|
+
if (a instanceof HTMLInputElement)
|
|
16
|
+
return TEXT_TYPES.has(a.type) ? a : null;
|
|
17
|
+
// a Field's own buttons (show password, clear) keep the eyes on their field
|
|
18
|
+
return a.closest("[data-sx-field]")?.querySelector("[data-sx-field-control]") ?? null;
|
|
19
|
+
}
|
|
20
|
+
/** Where the caret sits, in the viewport: a hidden copy of the text before it, measured in the field's own font. */
|
|
21
|
+
function caretPoint(el, mirror) {
|
|
22
|
+
const cs = getComputedStyle(el);
|
|
23
|
+
const r = el.getBoundingClientRect();
|
|
24
|
+
const end = el.selectionEnd ?? el.value.length;
|
|
25
|
+
const before = el.value.slice(0, end);
|
|
26
|
+
const lines = before.split("\n");
|
|
27
|
+
const last = lines[lines.length - 1] ?? "";
|
|
28
|
+
mirror.style.font = `${cs.fontStyle} ${cs.fontWeight} ${cs.fontSize} / ${cs.lineHeight} ${cs.fontFamily}`;
|
|
29
|
+
mirror.style.letterSpacing = cs.letterSpacing;
|
|
30
|
+
mirror.textContent = el instanceof HTMLInputElement && el.type === "password" ? "•".repeat([...last].length) : last;
|
|
31
|
+
const w = mirror.getBoundingClientRect().width;
|
|
32
|
+
const rtl = cs.direction === "rtl";
|
|
33
|
+
const raw = rtl ? r.right - parseFloat(cs.paddingRight) - w + el.scrollLeft : r.left + parseFloat(cs.paddingLeft) + w - el.scrollLeft;
|
|
34
|
+
const x = Math.max(r.left, Math.min(r.right, raw));
|
|
35
|
+
const line = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.5;
|
|
36
|
+
const y = el instanceof HTMLTextAreaElement ? Math.min(r.bottom, r.top + parseFloat(cs.paddingTop) + line * (lines.length - 0.5) - el.scrollTop) : r.top + r.height / 2;
|
|
37
|
+
return { x, y };
|
|
38
|
+
}
|
|
39
|
+
/** Anything in the form flagged wrong: by the app (`aria-invalid`), or by the browser once the user has been there. */
|
|
40
|
+
function flagged(scope) {
|
|
41
|
+
if (scope.querySelector('[aria-invalid="true"]'))
|
|
42
|
+
return true;
|
|
43
|
+
try {
|
|
44
|
+
return scope.querySelector(":user-invalid") !== null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Our own little character for a sign-in form: a round sprout on the form's top edge. Its eyes follow the caret in
|
|
52
|
+
* the field you type in; on a password it covers its eyes, and peeks when the password is shown; it looks worried while
|
|
53
|
+
* a field is wrong, and happy when you say so (`mood="happy"`). Decoration only: hidden from screen readers, it never
|
|
54
|
+
* takes the pointer and never covers a field. Under reduced motion its face changes without moving.
|
|
55
|
+
*
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <form id="sign-in"> <FormBuddy mood={done ? "happy" : undefined} /> <Field label="Email" … /> … </form>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function FormBuddy({ form, mood, size, className, style }) {
|
|
61
|
+
const root = useRef(null);
|
|
62
|
+
const said = useRef(mood);
|
|
63
|
+
said.current = mood;
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const el = root.current;
|
|
66
|
+
if (!el)
|
|
67
|
+
return;
|
|
68
|
+
const scope = (form ? document.getElementById(form) : null) ?? el.closest("form") ?? el.parentElement?.querySelector("form") ?? document;
|
|
69
|
+
const eyes = el.querySelector('[data-sx-buddy-part="eyes"]');
|
|
70
|
+
const mirror = document.createElement("span");
|
|
71
|
+
mirror.setAttribute("aria-hidden", "true");
|
|
72
|
+
Object.assign(mirror.style, { position: "fixed", insetBlockStart: "0", insetInlineStart: "0", visibility: "hidden", whiteSpace: "pre", pointerEvents: "none" });
|
|
73
|
+
document.body.appendChild(mirror);
|
|
74
|
+
let frame = 0;
|
|
75
|
+
const update = () => {
|
|
76
|
+
frame = 0;
|
|
77
|
+
const field = focusedField(scope);
|
|
78
|
+
let state = "idle";
|
|
79
|
+
let x = 0;
|
|
80
|
+
let y = 0;
|
|
81
|
+
if (field) {
|
|
82
|
+
const secret = field.type === "password" || field.closest("[data-sx-revealed]") !== null;
|
|
83
|
+
state = !secret ? "look" : field.type === "password" ? "cover" : "peek";
|
|
84
|
+
const at = caretPoint(field, mirror);
|
|
85
|
+
const box = eyes?.getBoundingClientRect();
|
|
86
|
+
if (box) {
|
|
87
|
+
x = Math.max(-1, Math.min(1, (at.x - (box.left + box.width / 2)) / 220));
|
|
88
|
+
y = Math.max(-1, Math.min(1, (at.y - (box.top + box.height / 2)) / 160));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
el.setAttribute("data-sx-buddy-state", state);
|
|
92
|
+
el.style.setProperty("--sx-buddy-x", x.toFixed(3));
|
|
93
|
+
el.style.setProperty("--sx-buddy-y", y.toFixed(3));
|
|
94
|
+
const face = said.current ?? (flagged(scope) ? "worried" : undefined);
|
|
95
|
+
if (face)
|
|
96
|
+
el.setAttribute("data-sx-mood", face);
|
|
97
|
+
else
|
|
98
|
+
el.removeAttribute("data-sx-mood");
|
|
99
|
+
};
|
|
100
|
+
const soon = () => {
|
|
101
|
+
if (!frame)
|
|
102
|
+
frame = requestAnimationFrame(update);
|
|
103
|
+
};
|
|
104
|
+
// focus moves (focusout runs before the next field has it: wait a frame), typing, the caret, a password shown or a
|
|
105
|
+
// field flagged by the app
|
|
106
|
+
const events = ["focusin", "focusout", "input", "keyup", "click", "invalid", "submit", "reset"];
|
|
107
|
+
for (const type of events)
|
|
108
|
+
scope.addEventListener(type, soon, true);
|
|
109
|
+
document.addEventListener("selectionchange", soon);
|
|
110
|
+
window.addEventListener("resize", soon);
|
|
111
|
+
const watch = new MutationObserver(soon);
|
|
112
|
+
watch.observe(scope, { subtree: true, attributes: true, attributeFilter: ["aria-invalid", "data-sx-revealed", "type"] });
|
|
113
|
+
const off = typeof IntersectionObserver === "undefined"
|
|
114
|
+
? null
|
|
115
|
+
: new IntersectionObserver((entries) => {
|
|
116
|
+
for (const entry of entries)
|
|
117
|
+
el.toggleAttribute("data-sx-buddy-off", !entry.isIntersecting);
|
|
118
|
+
});
|
|
119
|
+
off?.observe(el);
|
|
120
|
+
update();
|
|
121
|
+
return () => {
|
|
122
|
+
cancelAnimationFrame(frame);
|
|
123
|
+
for (const type of events)
|
|
124
|
+
scope.removeEventListener(type, soon, true);
|
|
125
|
+
document.removeEventListener("selectionchange", soon);
|
|
126
|
+
window.removeEventListener("resize", soon);
|
|
127
|
+
watch.disconnect();
|
|
128
|
+
off?.disconnect();
|
|
129
|
+
mirror.remove();
|
|
130
|
+
};
|
|
131
|
+
}, [form]);
|
|
132
|
+
// the app's own call on its face shows at once
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
const el = root.current;
|
|
135
|
+
if (!el)
|
|
136
|
+
return;
|
|
137
|
+
if (mood)
|
|
138
|
+
el.setAttribute("data-sx-mood", mood);
|
|
139
|
+
else if (el.getAttribute("data-sx-mood") === "happy")
|
|
140
|
+
el.removeAttribute("data-sx-mood");
|
|
141
|
+
}, [mood]);
|
|
142
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { href: styleHref("form-buddy", BUDDY_CSS), precedence: "sx", children: BUDDY_CSS }), _jsx("style", { href: styleHref("form-buddy-motion", BUDDY_MOTION), precedence: "sx", children: motionOnly(BUDDY_MOTION) }), _jsx("span", { ref: root, "data-sx-buddy": "", "data-sx-buddy-state": "idle", "data-sx-mood": mood, "aria-hidden": true, className: className, style: size ? { "--sx-buddy-size": size, ...style } : style, children: _jsxs("svg", { viewBox: "0 0 160 120", focusable: "false", children: [_jsx("path", { "data-sx-buddy-part": "stem", d: "M80 32c0-9 3-15 8-19" }), _jsx("path", { "data-sx-buddy-part": "leaf", d: "M88 13c8-8 19-7 22-3-6 7-16 9-22 3Z" }), _jsx("path", { "data-sx-buddy-part": "leaf", d: "M81 23c-6-8-14-8-17-5 4 6 12 8 17 5Z" }), _jsx("path", { "data-sx-buddy-part": "body", d: "M14 121c0-56 30-90 66-90s66 34 66 90Z" }), _jsxs("g", { "data-sx-buddy-part": "face", children: [_jsx("ellipse", { "data-sx-buddy-part": "cheek", cx: "40", cy: "95", rx: "9", ry: "5" }), _jsx("ellipse", { "data-sx-buddy-part": "cheek", cx: "120", cy: "95", rx: "9", ry: "5" }), _jsx("g", { "data-sx-buddy-part": "eyes", children: [60, 100].map((cx) => (_jsxs("g", { "data-sx-buddy-part": "eye", children: [_jsx("ellipse", { "data-sx-buddy-part": "white", cx: cx, cy: "74", rx: "13", ry: "15" }), _jsxs("g", { "data-sx-buddy-part": "pupil", children: [_jsx("circle", { cx: cx, cy: "75", r: "6.5" }), _jsx("circle", { "data-sx-buddy-part": "glint", cx: cx + 2.5, cy: "72", r: "2" })] })] }, cx))) }), _jsx("path", { "data-sx-buddy-part": "smile-eye", d: "M49 77q11-11 22 0M89 77q11-11 22 0" }), _jsx("path", { "data-sx-buddy-part": "brow", d: "M49 57l20-6M111 57l-20-6" }), _jsx("path", { "data-sx-buddy-part": "calm", d: "M72 97q8 6 16 0" }), _jsx("path", { "data-sx-buddy-part": "wobble", d: "M69 100q5.5-5 11 0t11 0" }), _jsx("path", { "data-sx-buddy-part": "grin", d: "M68 94q12 18 24 0Z" })] }), ["start", "end"].map((side) => (_jsxs("g", { "data-sx-buddy-part": "hand", "data-sx-side": side, children: [_jsx("ellipse", { cx: side === "start" ? 58 : 102, cy: "142", rx: "18", ry: "14" }), _jsx("path", { "data-sx-buddy-part": "finger", d: side === "start" ? "M51 133v7M58 131v8M65 133v7" : "M95 133v7M102 131v8M109 133v7" })] }, side)))] }) })] }));
|
|
143
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
export interface FormRevealProps {
|
|
3
|
+
/** Whether the fields show. */
|
|
4
|
+
readonly open: boolean;
|
|
5
|
+
/** A legend for the fields, read by screen readers with them. */
|
|
6
|
+
readonly label?: ReactNode;
|
|
7
|
+
readonly className?: string;
|
|
8
|
+
readonly style?: CSSProperties;
|
|
9
|
+
readonly children?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fields that open below a choice ("Send an invoice to another address"): the space eases open, the fields fade in.
|
|
13
|
+
* While closed, they sit in a disabled, inert fieldset: not submitted, not checked, not reachable. They keep what was
|
|
14
|
+
* typed, for when they open again. No code of its own: a Server Component can render it.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <Checkbox label="Invoice to another address" {...register("other")} />
|
|
18
|
+
* <FormReveal open={watch("other")}> <Field label="Street" {...register("street")} /> </FormReveal>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function FormReveal({ open, label, className, style, children }: FormRevealProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { styleHref } from "../../backgrounds/hosts/styleHref.js";
|
|
3
|
+
import { motionOnly } from "../../transitions/progress.js";
|
|
4
|
+
import { REVEAL_CSS, REVEAL_MOTION } from "../reveal.js";
|
|
5
|
+
import { filled } from "./shared.js";
|
|
6
|
+
/**
|
|
7
|
+
* Fields that open below a choice ("Send an invoice to another address"): the space eases open, the fields fade in.
|
|
8
|
+
* While closed, they sit in a disabled, inert fieldset: not submitted, not checked, not reachable. They keep what was
|
|
9
|
+
* typed, for when they open again. No code of its own: a Server Component can render it.
|
|
10
|
+
*
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Checkbox label="Invoice to another address" {...register("other")} />
|
|
13
|
+
* <FormReveal open={watch("other")}> <Field label="Street" {...register("street")} /> </FormReveal>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function FormReveal({ open, label, className, style, children }) {
|
|
17
|
+
const motion = motionOnly(REVEAL_MOTION);
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { href: styleHref("form-reveal", REVEAL_CSS), precedence: "sx", children: REVEAL_CSS }), _jsx("style", { href: styleHref("form-reveal-motion", motion), precedence: "sx", children: motion }), _jsx("div", { "data-sx-reveal": "", "data-sx-open": open ? "" : undefined, className: className, style: style, children: _jsxs("fieldset", { "data-sx-reveal-body": "", disabled: !open, inert: !open, children: [filled(label) ? _jsx("legend", { children: label }) : null, children] }) })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import type { FormEffect } from "../types.js";
|
|
3
|
+
/** The words FormSteps says, for your language. `{n}` is a step's number, `{total}` how many, `{title}` its title. */
|
|
4
|
+
export interface FormStepsLabels {
|
|
5
|
+
readonly progress?: string;
|
|
6
|
+
readonly status?: string;
|
|
7
|
+
readonly done?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Where the steps are, and the moves: for your own buttons, a summary, a step that jumps back. */
|
|
10
|
+
export declare function useFormSteps(): {
|
|
11
|
+
readonly step: number;
|
|
12
|
+
readonly count: number;
|
|
13
|
+
readonly next: () => Promise<boolean>;
|
|
14
|
+
readonly back: () => void;
|
|
15
|
+
readonly go: (step: number) => void;
|
|
16
|
+
};
|
|
17
|
+
export interface FormStepsRootProps {
|
|
18
|
+
/** The step shown (from 0), when you hold it. */
|
|
19
|
+
readonly step?: number;
|
|
20
|
+
readonly defaultStep?: number;
|
|
21
|
+
readonly onStepChange?: (step: number) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the step may be left forward: React Hook Form's `trigger(fieldsOf[step])` fits. Default: the browser's own
|
|
24
|
+
* checks on the step's fields (`required`, `type`, `pattern`), the first wrong one shown and focused.
|
|
25
|
+
*/
|
|
26
|
+
readonly validate?: (step: number) => boolean | Promise<boolean>;
|
|
27
|
+
/** `"focus"`: one question at a time, larger, Enter moving on. Default `"slide"`. */
|
|
28
|
+
readonly mode?: "slide" | "focus";
|
|
29
|
+
/** `stepSlide`, `stepProgress`. */
|
|
30
|
+
readonly effect?: FormEffect | readonly FormEffect[];
|
|
31
|
+
readonly labels?: FormStepsLabels;
|
|
32
|
+
readonly unstyled?: boolean;
|
|
33
|
+
readonly className?: string;
|
|
34
|
+
readonly style?: CSSProperties;
|
|
35
|
+
/** `FormSteps.Step` children (directly inside), with `FormSteps.Progress`, `Back` and `Next` anywhere around them. */
|
|
36
|
+
readonly children?: ReactNode;
|
|
37
|
+
}
|
|
38
|
+
export interface FormStepsStepProps {
|
|
39
|
+
/** Names the step in the progress and to screen readers. */
|
|
40
|
+
readonly title?: ReactNode;
|
|
41
|
+
readonly className?: string;
|
|
42
|
+
readonly children?: ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A form in steps. Only the current step shows; Next checks it (your `validate`, or the browser's own checks) before
|
|
46
|
+
* moving on, and the new step's first field takes the focus, with "Step 2 of 4: Address" read out. The box eases to the
|
|
47
|
+
* new step's height. Every step stays in the page (the others inert), so a plain form submits every field.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* <FormSteps.Root validate={(i) => trigger(FIELDS[i])} effect={[stepSlide(), stepProgress()]}>
|
|
51
|
+
* <FormSteps.Progress />
|
|
52
|
+
* <FormSteps.Step title="Account">…</FormSteps.Step>
|
|
53
|
+
* <FormSteps.Step title="Address">…</FormSteps.Step>
|
|
54
|
+
* <FormSteps.Back>Back</FormSteps.Back> <FormSteps.Next submit="Send">Next</FormSteps.Next>
|
|
55
|
+
* </FormSteps.Root>
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function FormStepsRoot(props: FormStepsRootProps): import("react").JSX.Element;
|
|
59
|
+
/** One step: a group named by its title. Only the current one shows; the others stay in the page, inert. */
|
|
60
|
+
export declare function FormStepsStep({ title, className, children }: FormStepsStepProps): import("react").JSX.Element;
|
|
61
|
+
export declare namespace FormStepsStep {
|
|
62
|
+
var sxFormStep: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** The steps as numbered marks on a rail that fills; a step done shows a check, the current one is `aria-current`. */
|
|
65
|
+
export declare function FormStepsProgress({ className }: {
|
|
66
|
+
readonly className?: string;
|
|
67
|
+
}): import("react").JSX.Element;
|
|
68
|
+
type ButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "type">;
|
|
69
|
+
/** Moves on, once the step checks out. On the last step it is the form's submit button, with the `submit` words. */
|
|
70
|
+
export declare function FormStepsNext({ submit, children, onClick, ...rest }: ButtonProps & {
|
|
71
|
+
readonly submit?: ReactNode;
|
|
72
|
+
}): import("react").JSX.Element;
|
|
73
|
+
/** Goes back a step, without checking it. Hidden on the first step. */
|
|
74
|
+
export declare function FormStepsBack({ children, onClick, ...rest }: ButtonProps): import("react").JSX.Element;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Children, createContext, isValidElement, useContext, useEffect, useId, useRef, useState, } from "react";
|
|
4
|
+
import { warnOnce } from "../../buttons/runtime/dev.js";
|
|
5
|
+
import { stillMotion } from "../../buttons/runtime/press.js";
|
|
6
|
+
import { useMorph } from "../runtime/useMorph.js";
|
|
7
|
+
import { STEPS_CSS } from "../steps.js";
|
|
8
|
+
import { EffectSheets } from "./choiceParts.js";
|
|
9
|
+
import { effectList, withUid } from "./shared.js";
|
|
10
|
+
const WORDS = { progress: "Progress", status: "Step {n} of {total}: {title}", done: "(done)" };
|
|
11
|
+
const Ctx = createContext(null);
|
|
12
|
+
const Index = createContext(-1);
|
|
13
|
+
function useSteps(part) {
|
|
14
|
+
const c = useContext(Ctx);
|
|
15
|
+
if (!c)
|
|
16
|
+
throw new Error(`[@sharedex/ui] FormSteps.${part} goes inside FormSteps.Root`);
|
|
17
|
+
return c;
|
|
18
|
+
}
|
|
19
|
+
/** Where the steps are, and the moves: for your own buttons, a summary, a step that jumps back. */
|
|
20
|
+
export function useFormSteps() {
|
|
21
|
+
const c = useSteps("useFormSteps");
|
|
22
|
+
return { step: c.at, count: c.count, next: c.next, back: c.back, go: c.go };
|
|
23
|
+
}
|
|
24
|
+
const LAZY = Symbol.for("react.lazy");
|
|
25
|
+
/**
|
|
26
|
+
* Whether a child is a `FormSteps.Step`. Children written in a Server Component reach this client component with a lazy
|
|
27
|
+
* reference as their type, not the function itself: unwrap it the way React does (its `_init`), then look for the
|
|
28
|
+
* Step's own mark (which also holds when the package is installed twice).
|
|
29
|
+
*/
|
|
30
|
+
function isStepElement(child) {
|
|
31
|
+
if (!isValidElement(child))
|
|
32
|
+
return false;
|
|
33
|
+
let type = child.type;
|
|
34
|
+
for (let i = 0; i < 4 && type && typeof type === "object" && type.$$typeof === LAZY; i++) {
|
|
35
|
+
try {
|
|
36
|
+
type = type._init(type._payload);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return type === FormStepsStep || (typeof type === "function" && type.sxFormStep === true);
|
|
43
|
+
}
|
|
44
|
+
const FIELDS = "input:not([type=hidden]), select, textarea";
|
|
45
|
+
const FOCUSABLE = `${FIELDS.replace("input:not([type=hidden])", "input:not([type=hidden]):not(:disabled)")}, [tabindex]:not([tabindex="-1"])`;
|
|
46
|
+
/** The browser's own checks on a step's fields: the first wrong one shows its message and takes the focus. */
|
|
47
|
+
function checkStep(step) {
|
|
48
|
+
if (!step)
|
|
49
|
+
return true;
|
|
50
|
+
const bad = [...step.querySelectorAll(FIELDS)].find((f) => !f.disabled && !f.checkValidity());
|
|
51
|
+
if (!bad)
|
|
52
|
+
return true;
|
|
53
|
+
bad.focus();
|
|
54
|
+
bad.reportValidity();
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A form in steps. Only the current step shows; Next checks it (your `validate`, or the browser's own checks) before
|
|
59
|
+
* moving on, and the new step's first field takes the focus, with "Step 2 of 4: Address" read out. The box eases to the
|
|
60
|
+
* new step's height. Every step stays in the page (the others inert), so a plain form submits every field.
|
|
61
|
+
*
|
|
62
|
+
* ```tsx
|
|
63
|
+
* <FormSteps.Root validate={(i) => trigger(FIELDS[i])} effect={[stepSlide(), stepProgress()]}>
|
|
64
|
+
* <FormSteps.Progress />
|
|
65
|
+
* <FormSteps.Step title="Account">…</FormSteps.Step>
|
|
66
|
+
* <FormSteps.Step title="Address">…</FormSteps.Step>
|
|
67
|
+
* <FormSteps.Back>Back</FormSteps.Back> <FormSteps.Next submit="Send">Next</FormSteps.Next>
|
|
68
|
+
* </FormSteps.Root>
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function FormStepsRoot(props) {
|
|
72
|
+
const { step, defaultStep = 0, onStepChange, validate, mode = "slide", effect, labels, unstyled, className, style, children } = props;
|
|
73
|
+
const effects = effectList(effect);
|
|
74
|
+
const words = { ...WORDS, ...labels };
|
|
75
|
+
const uid = `sx${useId().replace(/[^\w-]/g, "")}`;
|
|
76
|
+
const items = Children.toArray(children);
|
|
77
|
+
const steps = items.filter(isStepElement);
|
|
78
|
+
const count = steps.length;
|
|
79
|
+
const titles = steps.map((s) => s.props.title);
|
|
80
|
+
if (process.env.NODE_ENV !== "production") {
|
|
81
|
+
for (const e of effects)
|
|
82
|
+
if (!e.fits.includes("steps"))
|
|
83
|
+
warnOnce(`the effect ${e.id} is not made for FormSteps: it fits ${e.fits.join(", ")}.`);
|
|
84
|
+
if (count === 0)
|
|
85
|
+
warnOnce("FormSteps.Root has no FormSteps.Step directly inside it.");
|
|
86
|
+
}
|
|
87
|
+
const controlled = step !== undefined;
|
|
88
|
+
const [held, setHeld] = useState(defaultStep);
|
|
89
|
+
const at = Math.max(0, Math.min(count - 1, controlled ? step : held));
|
|
90
|
+
const [way, setWay] = useState(1);
|
|
91
|
+
const [leaving, setLeaving] = useState(null);
|
|
92
|
+
const [moved, setMoved] = useState(false);
|
|
93
|
+
const [said, setSaid] = useState("");
|
|
94
|
+
const root = useRef(null);
|
|
95
|
+
const view = useRef(null);
|
|
96
|
+
const stepEl = (i) => view.current?.querySelectorAll(":scope > [data-sx-step]")[i] ?? null;
|
|
97
|
+
useMorph(view, at, true);
|
|
98
|
+
const go = (i) => {
|
|
99
|
+
const to = Math.max(0, Math.min(count - 1, i));
|
|
100
|
+
if (to === at)
|
|
101
|
+
return;
|
|
102
|
+
setWay(to > at ? 1 : -1);
|
|
103
|
+
setLeaving(at);
|
|
104
|
+
setMoved(true);
|
|
105
|
+
if (!controlled)
|
|
106
|
+
setHeld(to);
|
|
107
|
+
onStepChange?.(to);
|
|
108
|
+
};
|
|
109
|
+
const next = async () => {
|
|
110
|
+
const ok = validate ? await validate(at) : checkStep(stepEl(at));
|
|
111
|
+
if (ok && at < count - 1)
|
|
112
|
+
go(at + 1);
|
|
113
|
+
return ok;
|
|
114
|
+
};
|
|
115
|
+
const back = () => go(at - 1);
|
|
116
|
+
// the step leaving: shown until its way-out ends (at once when nothing animates it)
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (leaving === null)
|
|
119
|
+
return;
|
|
120
|
+
const el = stepEl(leaving);
|
|
121
|
+
const drop = () => setLeaving(null);
|
|
122
|
+
if (!el || getComputedStyle(el).animationName === "none")
|
|
123
|
+
return drop();
|
|
124
|
+
el.addEventListener("animationend", drop, { once: true });
|
|
125
|
+
const t = setTimeout(drop, 1200);
|
|
126
|
+
return () => {
|
|
127
|
+
el.removeEventListener("animationend", drop);
|
|
128
|
+
clearTimeout(t);
|
|
129
|
+
};
|
|
130
|
+
}, [leaving]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
131
|
+
// a new step: its first field takes the focus, and the step is read out; never on load
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!moved)
|
|
134
|
+
return;
|
|
135
|
+
const el = stepEl(at);
|
|
136
|
+
el?.querySelector(FOCUSABLE)?.focus({ preventScroll: true });
|
|
137
|
+
root.current?.scrollIntoView({ block: "nearest", behavior: stillMotion() ? "auto" : "smooth" });
|
|
138
|
+
const title = titles[at];
|
|
139
|
+
setSaid(words.status.replace("{n}", String(at + 1)).replace("{total}", String(count)).replace("{title}", typeof title === "string" ? title : ""));
|
|
140
|
+
}, [at]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
141
|
+
// Enter in a field moves on (the last step's Enter submits, as the form's own)
|
|
142
|
+
const keys = (e) => {
|
|
143
|
+
const t = e.target;
|
|
144
|
+
if (e.key !== "Enter" || e.nativeEvent.isComposing || e.defaultPrevented || at >= count - 1)
|
|
145
|
+
return;
|
|
146
|
+
if (t.tagName !== "INPUT" || ["submit", "button", "reset", "checkbox", "radio", "file", "image"].includes(t.type))
|
|
147
|
+
return;
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
void next();
|
|
150
|
+
};
|
|
151
|
+
const c = { at, count, titles, leaving, effects, uid, words, next, back, go };
|
|
152
|
+
let placed = false;
|
|
153
|
+
let i = 0;
|
|
154
|
+
const body = items.map((item) => {
|
|
155
|
+
if (!isStepElement(item))
|
|
156
|
+
return item;
|
|
157
|
+
if (placed)
|
|
158
|
+
return null;
|
|
159
|
+
placed = true;
|
|
160
|
+
return (_jsx("div", { ref: view, "data-sx-steps-view": "", children: steps.map((s) => (_jsx(Index.Provider, { value: i++, children: s }, s.key ?? i))) }, "sx-steps-view"));
|
|
161
|
+
});
|
|
162
|
+
const vars = {
|
|
163
|
+
...withUid(Object.assign({}, ...effects.map((e) => e.vars)), uid),
|
|
164
|
+
"--sx-steps-way": way,
|
|
165
|
+
"--sx-steps-at": at,
|
|
166
|
+
"--sx-steps-count": count,
|
|
167
|
+
"--sx-steps-fill": count > 1 ? at / (count - 1) : 1,
|
|
168
|
+
...style,
|
|
169
|
+
};
|
|
170
|
+
return (_jsxs(Ctx.Provider, { value: c, children: [_jsx(EffectSheets, { name: "form-steps", base: STEPS_CSS, effects: effects }), _jsxs("div", { ref: root, "data-sx-steps": effects.map((e) => e.id).join(" "), "data-sx-mode": mode, "data-sx-moved": moved ? "" : undefined, "data-sx-styled": unstyled ? undefined : "", className: className, style: vars, onKeyDown: keys, children: [body, _jsx("p", { "data-sx-steps-sr": "", role: "status", children: said })] })] }));
|
|
171
|
+
}
|
|
172
|
+
/** One step: a group named by its title. Only the current one shows; the others stay in the page, inert. */
|
|
173
|
+
export function FormStepsStep({ title, className, children }) {
|
|
174
|
+
const c = useSteps("Step");
|
|
175
|
+
const i = useContext(Index);
|
|
176
|
+
const active = i === c.at;
|
|
177
|
+
return (_jsx("div", { role: "group", "aria-label": typeof title === "string" ? title : undefined, "data-sx-step": "", "data-sx-active": active ? "" : undefined, "data-sx-leaving": c.leaving === i && !active ? "" : undefined, inert: !active, className: className, children: children }));
|
|
178
|
+
}
|
|
179
|
+
FormStepsStep.sxFormStep = true;
|
|
180
|
+
/** The steps as numbered marks on a rail that fills; a step done shows a check, the current one is `aria-current`. */
|
|
181
|
+
export function FormStepsProgress({ className }) {
|
|
182
|
+
const c = useSteps("Progress");
|
|
183
|
+
return (_jsxs("div", { "data-sx-steps-progress": "", className: className, children: [_jsxs("span", { "data-sx-steps-rail": "", "aria-hidden": true, children: [_jsx("span", { "data-sx-steps-fill": "" }), c.effects.flatMap((e) => e.layers
|
|
184
|
+
.filter((l) => l.slot === "track")
|
|
185
|
+
.map((l, k) => _jsx("span", { "data-sx-steps-layer": e.id, "data-sx-steps-part": l.part, "data-sx-steps-keep": l.keep ? "" : undefined }, `${e.id}-${k}`)))] }), _jsx("ol", { "aria-label": c.words.progress, children: c.titles.map((title, i) => (_jsxs("li", { "data-sx-step-mark": "", "data-sx-done": i < c.at ? "" : undefined, "aria-current": i === c.at ? "step" : undefined, children: [_jsxs("span", { "data-sx-step-dot": "", "aria-hidden": true, children: [_jsx("span", { "data-sx-step-number": "", children: i + 1 }), _jsx("svg", { viewBox: "0 0 24 24", focusable: "false", children: _jsx("path", { d: "m5 12.5 4.5 4.5L19 7.5", pathLength: 1 }) })] }), _jsxs("span", { "data-sx-step-name": "", children: [title, i < c.at ? _jsxs("span", { "data-sx-steps-sr": "", children: [" ", c.words.done] }) : null] })] }, i))) })] }));
|
|
186
|
+
}
|
|
187
|
+
/** Moves on, once the step checks out. On the last step it is the form's submit button, with the `submit` words. */
|
|
188
|
+
export function FormStepsNext({ submit = "Send", children = "Next", onClick, ...rest }) {
|
|
189
|
+
const c = useSteps("Next");
|
|
190
|
+
// a new button for the last step: the click that moves onto it can't submit the form on its way
|
|
191
|
+
return c.at >= c.count - 1 ? (_jsx("button", { type: "submit", "data-sx-steps-next": "", onClick: onClick, ...rest, children: submit }, "sx-submit")) : (_jsx("button", { type: "button", "data-sx-steps-next": "", onClick: (e) => {
|
|
192
|
+
onClick?.(e);
|
|
193
|
+
if (!e.defaultPrevented)
|
|
194
|
+
void c.next();
|
|
195
|
+
}, ...rest, children: children }, "sx-next"));
|
|
196
|
+
}
|
|
197
|
+
/** Goes back a step, without checking it. Hidden on the first step. */
|
|
198
|
+
export function FormStepsBack({ children = "Back", onClick, ...rest }) {
|
|
199
|
+
const c = useSteps("Back");
|
|
200
|
+
return (_jsx("button", { type: "button", "data-sx-steps-back": "", hidden: c.at === 0, onClick: (e) => {
|
|
201
|
+
onClick?.(e);
|
|
202
|
+
if (!e.defaultPrevented)
|
|
203
|
+
c.back();
|
|
204
|
+
}, ...rest, children: children }));
|
|
205
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
export interface FormSwapProps {
|
|
3
|
+
/** The second view shows (the success message, the sign-up form, the card's back). */
|
|
4
|
+
readonly swapped: boolean;
|
|
5
|
+
readonly first: ReactNode;
|
|
6
|
+
readonly second: ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* `"success"`: the form gives way to a message, a check drawing itself; `"panel"`: two forms side by side, a panel
|
|
9
|
+
* sliding over the one not in use (stacked in a box under 36rem); `"flip"`: a card turned over. Default `"success"`.
|
|
10
|
+
*/
|
|
11
|
+
readonly look?: "success" | "panel" | "flip";
|
|
12
|
+
/** With `look="panel"`: what the panel shows over the second view and over the first (a line, a button to swap). */
|
|
13
|
+
readonly panel?: readonly [ReactNode, ReactNode];
|
|
14
|
+
readonly unstyled?: boolean;
|
|
15
|
+
readonly className?: string;
|
|
16
|
+
readonly style?: CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Two views in one place: a form and its success message, a sign-in and a sign-up form, a card's two sides. The one not
|
|
20
|
+
* shown is unseen and inert. On a swap the box eases to the new view's height and the focus moves into it: onto the
|
|
21
|
+
* success message itself (read out), or the new form's first field.
|
|
22
|
+
*
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <FormSwap swapped={sent} first={<ContactForm onSent={() => setSent(true)} />} second={<p>Thanks: we will be in touch.</p>} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function FormSwap({ swapped, first, second, look, panel, unstyled, className, style }: FormSwapProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { styleHref } from "../../backgrounds/hosts/CssHost.js";
|
|
5
|
+
import { motionOnly } from "../../transitions/progress.js";
|
|
6
|
+
import { useMorph } from "../runtime/useMorph.js";
|
|
7
|
+
import { SWAP_CSS, SWAP_MOTION } from "../swap.js";
|
|
8
|
+
const FOCUSABLE = "input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled), a[href]";
|
|
9
|
+
/**
|
|
10
|
+
* Two views in one place: a form and its success message, a sign-in and a sign-up form, a card's two sides. The one not
|
|
11
|
+
* shown is unseen and inert. On a swap the box eases to the new view's height and the focus moves into it: onto the
|
|
12
|
+
* success message itself (read out), or the new form's first field.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <FormSwap swapped={sent} first={<ContactForm onSent={() => setSent(true)} />} second={<p>Thanks: we will be in touch.</p>} />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function FormSwap({ swapped, first, second, look = "success", panel, unstyled, className, style }) {
|
|
19
|
+
const stage = useRef(null);
|
|
20
|
+
const [moved, setMoved] = useState(false);
|
|
21
|
+
const was = useRef(swapped);
|
|
22
|
+
useMorph(stage, swapped, look !== "flip");
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (was.current === swapped)
|
|
25
|
+
return;
|
|
26
|
+
was.current = swapped;
|
|
27
|
+
setMoved(true);
|
|
28
|
+
const view = stage.current?.querySelector(`[data-sx-swap-view="${swapped ? "second" : "first"}"]`);
|
|
29
|
+
if (!view)
|
|
30
|
+
return;
|
|
31
|
+
if (look === "success" && swapped)
|
|
32
|
+
view.focus({ preventScroll: true });
|
|
33
|
+
else
|
|
34
|
+
(view.querySelector(FOCUSABLE) ?? view).focus({ preventScroll: true });
|
|
35
|
+
}, [swapped, look]);
|
|
36
|
+
const motion = motionOnly(SWAP_MOTION);
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { href: styleHref("form-swap", SWAP_CSS), precedence: "sx", children: SWAP_CSS }), _jsx("style", { href: styleHref("form-swap-motion", motion), precedence: "sx", children: motion }), _jsx("div", { "data-sx-swap": "", "data-sx-look": look, "data-sx-swapped": swapped ? "" : undefined, "data-sx-moved": moved ? "" : undefined, "data-sx-styled": unstyled ? undefined : "", className: className, style: style, children: _jsxs("div", { ref: stage, "data-sx-swap-stage": "", children: [_jsx("div", { "data-sx-swap-view": "first", "data-sx-active": swapped ? undefined : "", inert: swapped, tabIndex: -1, children: first }), _jsxs("div", { "data-sx-swap-view": "second", "data-sx-active": swapped ? "" : undefined, inert: !swapped, tabIndex: -1, children: [look === "success" ? (_jsx("span", { "data-sx-swap-check": "", "aria-hidden": true, children: _jsxs("svg", { viewBox: "0 0 48 48", focusable: "false", children: [_jsx("circle", { cx: "24", cy: "24", r: "21" }), _jsx("path", { d: "m14 25 7 7 14-15", pathLength: 1 })] }) })) : null, second] }), look === "panel" && panel ? (_jsxs("div", { "data-sx-swap-panel": "", children: [_jsx("div", { "data-sx-swap-face": "first", "data-sx-active": swapped ? undefined : "", inert: swapped, children: panel[0] }), _jsx("div", { "data-sx-swap-face": "second", "data-sx-active": swapped ? "" : undefined, inert: !swapped, children: panel[1] })] })) : null] }) })] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef, type CSSProperties } from "react";
|
|
2
|
+
/** The words an InlineEdit says, for your language. `{label}` is its label. */
|
|
3
|
+
export interface InlineEditLabels {
|
|
4
|
+
readonly edit?: string;
|
|
5
|
+
readonly saved?: string;
|
|
6
|
+
readonly failed?: string;
|
|
7
|
+
}
|
|
8
|
+
interface InlineEditOwnProps {
|
|
9
|
+
/** What the text is ("Title"): names the field and its edit button ("Edit Title"). The text alone doesn't say. */
|
|
10
|
+
readonly label: string;
|
|
11
|
+
/** `"textarea"` for several lines: Enter adds a line there, Ctrl / ⌘ + Enter keeps the edit. Default `"input"`. */
|
|
12
|
+
readonly as?: "input" | "textarea";
|
|
13
|
+
readonly value?: string;
|
|
14
|
+
readonly defaultValue?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Called with the new text once an edit is kept (Enter, or leaving the field). Return a promise and the text shows as
|
|
17
|
+
* saving until it settles; if it fails, the text goes back and the field says so.
|
|
18
|
+
*/
|
|
19
|
+
readonly onSave?: (value: string) => void | Promise<unknown>;
|
|
20
|
+
/** Called when an edit is thrown away (Esc). */
|
|
21
|
+
readonly onCancel?: () => void;
|
|
22
|
+
/** Shown, quieter, while the text is empty. */
|
|
23
|
+
readonly placeholder?: string;
|
|
24
|
+
/** Leaving the field keeps the edit. `false`: leaving throws it away, only Enter keeps it. Default `true`. */
|
|
25
|
+
readonly saveOnBlur?: boolean;
|
|
26
|
+
readonly labels?: InlineEditLabels;
|
|
27
|
+
readonly unstyled?: boolean;
|
|
28
|
+
/** Goes on the input. */
|
|
29
|
+
readonly className?: string;
|
|
30
|
+
/** Goes on the text (the button you click to edit). */
|
|
31
|
+
readonly textClassName?: string;
|
|
32
|
+
readonly rootClassName?: string;
|
|
33
|
+
readonly style?: CSSProperties;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Every other prop goes on the real input, `ref` included: `name`, `onChange`, `onBlur`, `required`, `maxLength`, so
|
|
37
|
+
* `{...register("title")}` spreads onto it, and a plain form submits the text even while nobody edits it.
|
|
38
|
+
*/
|
|
39
|
+
export type InlineEditProps = InlineEditOwnProps & Omit<ComponentPropsWithRef<"input">, keyof InlineEditOwnProps | "type">;
|
|
40
|
+
/**
|
|
41
|
+
* Text that turns into a field and back: a click (or Enter on it) opens the field with the text selected, Enter or
|
|
42
|
+
* leaving it keeps the edit, Esc throws it away and gives the text back. The real input is always in the page, so a
|
|
43
|
+
* form submits it; at rest it is inert and unseen.
|
|
44
|
+
*
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <InlineEdit label="Title" defaultValue={page.title} onSave={(title) => save({ title })} />
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function InlineEdit(props: InlineEditProps): import("react").JSX.Element;
|
|
50
|
+
export {};
|