74
Instant Dynamic Forms with # states

Instant Dynamic Forms with #states

Embed Size (px)

Citation preview

Page 1: Instant Dynamic Forms with #states

InstantDynamic Forms

with#states

Page 2: Instant Dynamic Forms with #states

Konstantin Käfer2

2006

Page 3: Instant Dynamic Forms with #states

#states 3

Page 4: Instant Dynamic Forms with #states

4

Page 5: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 6: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 7: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 8: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 9: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 10: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 11: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 12: Instant Dynamic Forms with #states

5

Anatomy of a state

$form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Page 13: Instant Dynamic Forms with #states

Declarative definition6

➜Expanded when the element “payment”is checked

'expanded' => array( '[name="payment"]' => array('checked' => TRUE)),

Page 14: Instant Dynamic Forms with #states

7

Dependencies$form['payment_information'] = array(... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),); <fieldset>

<input type="checkbox">

Depends on Influences

Page 15: Instant Dynamic Forms with #states

Targeting elements◆ Uses plain CSS selectors with jQuery◆ [name="payment"]#edit-payment.payment :checkbox, #edit-payment

◆ Don’t use #selector for auto-assigned IDs

8

Page 16: Instant Dynamic Forms with #states

States◆ Arbitrary names are possible◆ visible irrelevant confirmed

checked valid important

◆ Prefixing with ! negates◆ visible = !invisible

invisible = !visible

9

Page 17: Instant Dynamic Forms with #states

State aliases◆ Associate custom aliases◆ Drupal.states.State.aliases ['unimportant'] = '!important';

◆ enabled = !disabled invisible = !visible

invalid = !valid untouched = !touched

optional = !required filled = !empty

unchecked = !checked irrelevant = !relevant

expanded = !collapsed readwrite = !readonly

10

Primary name

Page 18: Instant Dynamic Forms with #states

Drawbacks◆ Doesn’t support OR and XOR

11

Page 19: Instant Dynamic Forms with #states

Drawbacks◆ Doesn’t support OR and XOR

11

Patch!drupal.org/node/735528

Page 20: Instant Dynamic Forms with #states

AND operator12

'disabled' => array( '[name="ccv"]' => array( 'invalid' => TRUE ), '[name="card_number"]' => array( 'invalid' => TRUE ), ),

Page 21: Instant Dynamic Forms with #states

OR operator13

'disabled' => array( array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ),),

Page 22: Instant Dynamic Forms with #states

OR operator13

'disabled' => array( array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ),),

Numeric keys

Page 23: Instant Dynamic Forms with #states

XOR operator14

'disabled' => array('xor', array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ),),

Page 24: Instant Dynamic Forms with #states

XOR operator14

'disabled' => array('xor', array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ),),

Operator

Page 25: Instant Dynamic Forms with #states

Drawbacks◆ Doesn’t support OR and XOR

15

Patch!drupal.org/node/735528

Page 26: Instant Dynamic Forms with #states

Drawbacks◆ Doesn’t support OR and XOR

◆ Doesn’t support radio buttons

15

Patch!drupal.org/node/735528

Page 27: Instant Dynamic Forms with #states

Drawbacks◆ Doesn’t support OR and XOR

◆ Doesn’t support radio buttons

15

Patch!drupal.org/node/735528

Extend!

Page 28: Instant Dynamic Forms with #states

Triggers

Page 29: Instant Dynamic Forms with #states

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

Default Triggers17

Page 30: Instant Dynamic Forms with #states

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

Default Triggers17

Native DOM event

Page 31: Instant Dynamic Forms with #states

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

Default Triggers17

Page 32: Instant Dynamic Forms with #states

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

Default Triggers17

Value function

Page 33: Instant Dynamic Forms with #states

Default Triggers18

Initialization ExecutionDrupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Page 34: Instant Dynamic Forms with #states

Default Triggers18

Initialization ExecutionDrupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Page 35: Instant Dynamic Forms with #states

Default Triggers18

Initialization ExecutionDrupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ...};

$('#element').bind('change', function() { ... });

Page 36: Instant Dynamic Forms with #states

Multiple Triggers19

Drupal.states.Trigger.states = { ... value: { 'keyup': function () { return this.val(); }, 'change': function () { return this.val(); } }, ...};

Page 37: Instant Dynamic Forms with #states

Multiple Triggers20

Drupal.states.Trigger.states = { ... value: { 'keyup change': function () { return this.val(); } }, ...};

Page 38: Instant Dynamic Forms with #states

Custom Triggers21

'#states' => array( 'disabled' => array( '[name="delayed"]' => array('value' => 'foo') ),),

Page 39: Instant Dynamic Forms with #states

Custom Triggers22

Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } };

... };

Page 40: Instant Dynamic Forms with #states

Custom Triggers22

Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } };

... };

Page 41: Instant Dynamic Forms with #states

Custom Triggers22

Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } };

... };

Page 42: Instant Dynamic Forms with #states

Custom Triggers22

Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } };

... };

Page 43: Instant Dynamic Forms with #states

Custom Triggers22

Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } };

... };

Page 44: Instant Dynamic Forms with #states

Custom Triggers23

Drupal.states.Trigger.states.delayedValue = function(element) { ...

element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };

Page 45: Instant Dynamic Forms with #states

Custom Triggers23

Drupal.states.Trigger.states.delayedValue = function(element) { ...

element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };

Page 46: Instant Dynamic Forms with #states

Custom Triggers23

Drupal.states.Trigger.states.delayedValue = function(element) { ...

element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };

Page 47: Instant Dynamic Forms with #states

Custom Triggers23

Drupal.states.Trigger.states.delayedValue = function(element) { ...

element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };

Page 48: Instant Dynamic Forms with #states

Custom Triggers23

Drupal.states.Trigger.states.delayedValue = function(element) { ...

element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };

Page 49: Instant Dynamic Forms with #states

Custom Triggers24

Drupal.states.Trigger.states.toggle = function(element) { var value = true, oldValue = undefined; var trigger = function() { value = !value; element.trigger({ type: 'state:toggle', value: value, oldValue: oldValue }); oldValue = value; };

setInterval(trigger, 1000); Drupal.states.postponed.push(trigger); };

Page 50: Instant Dynamic Forms with #states

Comparisons

Page 51: Instant Dynamic Forms with #states

$form['payment_information'] = array( ... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Comparisons26

Page 52: Instant Dynamic Forms with #states

$form['payment_information'] = array( ... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ),);

Comparisons26

===

Page 53: Instant Dynamic Forms with #states

Advanced Comparisons27

states.Dependant.comparisons = { 'RegExp': function (reference, value) { return reference.test(value); }, 'Function': function (reference, value) { return reference(value); }};

Page 54: Instant Dynamic Forms with #states

Advanced Comparisons27

states.Dependant.comparisons = { 'RegExp': function (reference, value) { return reference.test(value); }, 'Function': function (reference, value) { return reference(value); }}; Prototype name

Page 55: Instant Dynamic Forms with #states

JSON only allowsstrings and numbers 28

Page 56: Instant Dynamic Forms with #states

:( 29

Page 57: Instant Dynamic Forms with #states

Advanced Comparisons30

'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(\d{4}[ -]*){4}$'), ),),

'invalid' => array( '[name="card_number"]' => array( '!value' => '0000 0000 0000 0000', ),),

Page 58: Instant Dynamic Forms with #states

Advanced Comparisons30

'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(\d{4}[ -]*){4}$'), ),),

'invalid' => array( '[name="card_number"]' => array( '!value' => '0000 0000 0000 0000', ),),

Page 59: Instant Dynamic Forms with #states

Advanced Comparisons31

Drupal.states.Dependant.comparisons.Object = function(reference, value) { if ('regex' in reference) { return RegExp(reference.regex, ↵ reference.flags).test(value); } else { return reference.indexOf(value) !== false; } };

Page 60: Instant Dynamic Forms with #states

Advanced Comparisons32

'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(\d{4}[ -]*){4}$'), ),),

Page 61: Instant Dynamic Forms with #states

Transitions

Page 62: Instant Dynamic Forms with #states

State changes◆ Transition an element from one state

to another

34

Direct Indirect◆ Triggered by user◆ Notify listeners

◆ Triggered by other element

◆ Transition element

Page 63: Instant Dynamic Forms with #states

State changes35

$(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); }});

Page 64: Instant Dynamic Forms with #states

State changes35

$(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); }});

Page 65: Instant Dynamic Forms with #states

State changes35

$(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); }});

Page 66: Instant Dynamic Forms with #states

State changes35

$(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); }});

Page 67: Instant Dynamic Forms with #states

State changes35

$(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); }});

Page 68: Instant Dynamic Forms with #states

State changes

◆ Event bubbling allows overwriting handlers for specific regions

◆ CSS selectors allow overwriting handlers for specific elements

36

document

<html>

<body>

<div id="body">

<div class="element">

<input type="text">

Page 69: Instant Dynamic Forms with #states

Future Work

Page 70: Instant Dynamic Forms with #states

Domain-specific language38

state_of('[name="baz"]') ->is('checked') ->when('[name="bar"]')->checked() ->and('[name="foo"]')->value('foo') ->orWhen('[name="bar"]')->unchecked()

->is('disabled') ->when('[name="bar"]')->unchecked()

->is('invisible') ->when('[name="foo"]')->empty();

Ideas?

Page 71: Instant Dynamic Forms with #states

Copy & Paste support 39

Page 72: Instant Dynamic Forms with #states

Multi-value support40

// The value of at least one element is true.{'any': true}

// At least two elements are true.{'n > 2': true}

// The third element is false.{'2': false}

Page 73: Instant Dynamic Forms with #states

Extended values41

// The value is greater than 8 or smaller than 5.[ {'>': 8}, {'<': 5} ]

// At least two elements are between 5 and 8.{'n > 2': {'>': 5, '<': 8}}

// The sum of the values of all elements is// greater than 10.{'sum': {'>=': 10}}