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. <PendulumFields
8. 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 components
4.<PendulumFields
5. fields={[{ name: 'username' }, { name: 'email' }]}
6. showBackground={false}
7. renderField={({ field, value, onChange, onKeyDown, energyState }) => (
8. <div className={`my-field ${energyState}`}>
9. <MyCustomInput
10. 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.// Cleanup
15.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)