return function () {
$intro = '<p class="docs-demo-intro">Filter, sort and page the shipped sample products with <code>table()</code> on an array:
the request values pass a whitelist first (<code>sample_products::params()</code>: known colours and sort keys only, numbers clamped,
search text cut to 40 characters), are applied in PHP, and <code>table2</code> renders and pages the result
(<code>pager_records_on_page</code>). The same URL with <code>?page=2</code> works without JavaScript.
Classes: <code>yf_table2</code>, <code>yf_form2</code>, <code>yf_divide_pages</code>.</p>';
$products = _class('sample_products', YF_PATH . '.dev/samples/classes/');
$all = $products->load();
$p = $products->params($_GET, $all);
$rows = $products->apply($all, $p);
$self = url('/@object/@action/' . urlencode('table/products'));
$filter = form($p, ['method' => 'get', 'csrf' => false, 'action' => $self, 'class' => 'form-inline docs-demo-filter', 'no_label' => 1])
->row_start()
->text('q', ['placeholder' => 'Name contains', 'class_add' => 'input-medium'])
->select_box('colour', ['' => 'Any colour'] + $products->colours($all), ['class_add' => 'input-medium'])
->number('price_min', ['placeholder' => 'min $', 'class_add' => 'input-small'])
->number('price_max', ['placeholder' => 'max $', 'class_add' => 'input-small'])
->select_box('order', array_combine(array_keys($products->ORDER_FIELDS), ['Sort: name', 'Sort: price', 'Sort: sold', 'Sort: viewed', 'Sort: date']), ['class_add' => 'input-medium'])
->select_box('dir', ['asc' => 'Ascending', 'desc' => 'Descending'], ['class_add' => 'input-medium'])
->submit(['value' => 'Filter'])
->link('Reset', $self, ['class' => 'btn btn-default btn-sm'])
->row_end();
// a filter bar: the stacked row spans the whole width instead of sitting right of an empty label column
css('.docs-demo-filter .controls, .docs-demo-filter .form-group > [class*="offset-"] { margin-left:0; width:100%; max-width:100%; flex:0 0 100%; }');
$table = table($rows, [
'pager_records_on_page' => 5,
// the pager of array data takes its base url from extra.url_path (the demo id holds a slash, which the default
// ./?object=&action=&id= link would turn into path segments); the current GET values are appended to it
'extra' => ['url_path' => $self . '?'],
'no_records_html' => 'Nothing matches this filter.',
'condensed' => 1,
])
->text('id')
->text('name')
->text('colour')
->text('price')
->text('sold')
->text('viewed')
->date('add_date', 'Added');
return $intro . $filter . '<p class="text-muted">' . count($rows) . ' of ' . count($all) . ' products</p>' . $table;
};