Pendulum Fields
Form fields that swing like pendulums powered by your keystrokes. Keep typing to maintain momentum. If any pendulum stops, you can't submit! Later fields decay faster, so you'll need to juggle between them.
Demo
Type in any field to boost its momentum. Keep all pendulums swinging to enable the submit button!
React
React
1.import { PendulumFields } from 'anti-ai-ui';2.3.function App() {4. const [canSubmit, setCanSubmit] = useState(false);5.6. return (7. <PendulumFields8. fields={[9. { name: 'username', label: 'Username', type: 'text' },10. { name: 'email', label: 'Email', type: 'email' },11. { name: 'password', label: 'Password', type: 'password' },12. ]}13. onAllMoving={(allMoving) => setCanSubmit(allMoving)}14. />15. );16.}
Custom Field Rendering (React)
React - Custom Rendering
1.import { PendulumFields } from 'anti-ai-ui';2.3.// Use your own input components4.<PendulumFields5. fields={[{ name: 'username' }, { name: 'email' }]}6. showBackground={false}7. renderField={({ field, value, onChange, onKeyDown, energyState }) => (8. <div className={`my-field ${energyState}`}>9. <MyCustomInput10. name={field.name}11. value={value}12. onChange={(e) => onChange(e.target.value)}13. onKeyDown={onKeyDown}14. />15. {energyState === 'stopped' && <span>Type to restart!</span>}16. </div>17. )}18./>
Vanilla JS
Vanilla JS
1.import { createPendulumFields } from 'anti-ai-ui/vanilla';2.3.const form = createPendulumFields({4. container: document.getElementById('container'),5. fields: [6. { name: 'username', label: 'Username', type: 'text' },7. { name: 'email', label: 'Email', type: 'email' },8. ],9. onAllMoving: (allMoving) => {10. submitBtn.disabled = !allMoving;11. },12.});13.14.// Cleanup15.form.destroy();
Custom Field Rendering (Vanilla)
Vanilla JS - Custom Rendering
1.import { createPendulumFields } from 'anti-ai-ui/vanilla';2.3.const form = createPendulumFields({4. container: document.getElementById('container'),5. fields: [{ name: 'username' }, { name: 'email' }],6. showBackground: false,7. renderField: ({ field, energyState, boostEnergy, setValue }) => {8. const div = document.createElement('div');9. div.className = 'my-custom-field';10.11. const input = document.createElement('input');12. input.name = field.name;13. input.className = energyState; // 'healthy', 'dying', or 'stopped'14. input.addEventListener('keydown', boostEnergy);15. input.addEventListener('input', (e) => setValue(e.target.value));16.17. div.appendChild(input);18. return div;19. },20.});
Props
| Prop | Type | Default | Description |
|---|---|---|---|
fields | array | - | Array of field configs with name, label, type, placeholder |
renderField | function | - | Custom render function for fields (see examples above) |
showBackground | boolean | true | Whether to show the default dark gradient background |
maxAngle | number | 40 | Maximum swing angle in degrees at full energy |
energyDecay | number | 0.000095 | Energy lost per millisecond |
energyBoost | number | 0.15 | Energy gained per keystroke (0-1 scale) |
decayMultiplier | number | 0.5 | Extra decay per field index (later fields decay faster) |
onChange | function | - | Callback when form values change |
onAllMoving | function | - | Callback with boolean when all pendulums moving status changes |
RenderField Props
When using renderField, you receive these props:
| Prop | Type | Description |
|---|---|---|
field | object | The field config (name, label, type, placeholder) |
index | number | Index of the field in the array |
energy | number | Current energy level (0-1) |
energyState | string | 'healthy', 'dying', or 'stopped' |
value | string | Current input value (React only) |
onChange | function | Call with new value when input changes (React) |
onKeyDown | function | Call on keydown to boost energy (React) |
boostEnergy | function | Call on keydown to boost energy (Vanilla) |
setValue | function | Call with new value when input changes (Vanilla) |
getValue | function | Get current value (Vanilla) |