yf_html5fw_bs5 class

Twitter bootstrap v5 html5 abstraction driver.

Fileplugins/html/classes/html5fw/yf_html5fw_bs5.class.php
Use_class('html5fw_bs5', 'classes/html5fw/')
Extendsyf_html5fw_bs3 → yf_html5fw_empty

Public properties

A site sets them in its config with $PROJECT_CONF['html5fw_bs5']['NAME'] = ...;

PropertyDefaultDescription
$CLASS_ADDON 'input-group-text'
$CLASS_CONTROLS 'col-md-9'
$CLASS_CONTROLS_BUTTONS 'col-md-9'
$CLASS_CONTROL_INVALID
string
'is-invalid' validation state classes Bootstrap 5 styles on the control itself
$CLASS_CONTROL_VALID 'is-valid'
$CLASS_DESC ''
$CLASS_EDIT_ICON 'bi bi-pencil'
$CLASS_ERROR 'has-error is-invalid'
$CLASS_FEEDBACK 'invalid-feedback'
$CLASS_FLOATING
string
'form-floating' wrapper of a field with the `floating` option: the control, then its label
$CLASS_FORM_GROUP 'mb-3 row'
$CLASS_HELP 'form-text'
$CLASS_HELP_INVALID
string
'invalid-feedback d-block' help text of a field with an error (d-block: an input group sits between the control and the message)
$CLASS_INFO 'has-info'
$CLASS_INPUT_APPEND ''
$CLASS_INPUT_GROUP 'input-group'
$CLASS_INPUT_GROUP_BTN
string
'' Bootstrap 5 puts input group buttons in the group as they are
$CLASS_INPUT_PREPEND ''
$CLASS_LABEL 'col-form-label col-md-3'
$CLASS_NO_LABEL ' offset-md-3'
$CLASS_NO_LABEL_BUTTONS ' offset-md-3'
$CLASS_STACKED_CONTROLS
string
'd-flex flex-wrap align-items-start gap-2' controls of a form2 row_start() row: its stacked items sit side by side, as the bs3 CSS floats them
$CLASS_STACKED_ITEM 'stacked-item'
$CLASS_SUCCESS 'has-success is-valid'
$CLASS_WARNING 'has-warning'

Inherited properties

from yf_html5fw_bs3: $CLASS_INPUT_GROUP_SIZE

Public methods 5

form_dd_row

public function form_dd_row($content, $extra = [], $replace = [], $form = [])

Form row using dl row, dt col-sm-3, dd col-sm-9.

    public function form_dd_row($content, $extra = [], $replace = [], $form = [])
    {
        $class_wrapper = ($extra['class_wrapper'] ?? null) ?: 'row';
        if ($extra['class_add_wrapper'] ?? null) {
            $class_wrapper .= ' ' . $extra['class_add_wrapper'];
        }
        $class_dd = ($extra['class_dd'] ?? null) ?: 'col-sm-9';
        if ($extra['class_add_dd'] ?? null) {
            $class_dd .= ' ' . $extra['class_add_dd'];
        }
        $row_start = ! ($extra['wide'] ?? null) ? '<dl class="' . $class_wrapper . '">' . PHP_EOL . '<dt class="col-sm-3">' . t($extra['desc'] ?? '') . '</dt>' . PHP_EOL : '';
        $before_content_html = '<dd class="' . $class_dd . '">';
        $after_content_html = '</dd>';
        $row_end = '</dl>' . PHP_EOL;

        if ($extra['edit_link'] ?? null) {
            if (MAIN_TYPE_ADMIN && main()->ADMIN_GROUP != 1 && ! _class('admin_methods')->_admin_link_is_allowed($extra['edit_link'])) {
                $extra['edit_link'] = '';
            }
        }
        $edit_link_html = ($extra['edit_link'] ?? null) ? ' <a href="' . _escape_tpl_braces(_htmlchars($extra['edit_link'])) . '" class="' . $this->CLASS_EDIT_LINK . '"><i class="' . $this->CLASS_EDIT_ICON . '"></i> ' . t('Edit') . '</a>' . PHP_EOL : '';
        $link_name_html = (($extra['link_url'] ?? null) && ($extra['link_name'] ?? null)) ? ' <a href="' . $extra['link_url'] . '" class="' . $this->CLASS_LINK_URL . '">' . t($extra['link_name']) . '</a>' . PHP_EOL : '';
        $inline_tip_html = ($extra['tip'] ?? null) ? ' ' . tip($extra['tip'], $replace) : '';

        if ($extra['only_row_start'] ?? null) {
            return $row_start . $before_content_html;
        } elseif ($extra['only_row_end'] ?? null) {
            return $after_content_html . $row_end;
        } elseif ($extra['stacked'] ?? null) {
            return $content . PHP_EOL . $edit_link_html . $link_name_html . $inline_tip_html;
        }
        return $row_start
                    . $before_content_html . $content . PHP_EOL
                    . $edit_link_html . $link_name_html . $inline_tip_html . $after_content_html
                    . $this->_add_rich_editor($extra, $replace, $form)
                . $row_end;
    }

form_row

public function form_row($content, $extra = [], $replace = [], $form = [])

Bootstrap 5 row: the bs3 row markup with the validation state on the control (`is-invalid` / `is-valid` on the element named like the field) and the error text as `invalid-feedback`; the row wrapper keeps its state classes.

    public function form_row($content, $extra = [], $replace = [], $form = [])
    {
        $stacked_row = is_object($form) && isset($form->CLASS_STACKED_ROW) ? (string) $form->CLASS_STACKED_ROW : 'stacked-row';
        if (($extra['only_row_start'] ?? null) && $stacked_row !== '' && in_array($stacked_row, explode(' ', (string) ($extra['class_add_form_group'] ?? '')), true)) {
            $extra['class_add_controls'] = trim(($extra['class_add_controls'] ?? '') . ' ' . $this->CLASS_STACKED_CONTROLS);
        }
        if ($extra['floating'] ?? null) {
            [$content, $extra] = $this->_floating($content, $extra, $replace, $form);
        }
        $name = (string) ($extra['name'] ?? '');
        $state = $name !== '' ? $this->_field_state($extra, $name) : '';
        if ($state === '') {
            return parent::form_row($content, $extra, $replace, $form);
        }
        $content = $this->_add_control_class($content, $name, $state === 'errors' ? $this->CLASS_CONTROL_INVALID : $this->CLASS_CONTROL_VALID);
        if ($state !== 'errors') {
            return parent::form_row($content, $extra, $replace, $form);
        }
        $help = $this->CLASS_HELP;
        $this->CLASS_HELP = $this->CLASS_HELP_INVALID;
        try {
            return parent::form_row($content, $extra, $replace, $form);
        } finally {
            $this->CLASS_HELP = $help;
        }
    }

Methods starting with _ hooks and helpers; not routable as page actions

public function _add_control_class($html, $name, $class)

Add a class to the input / select / textarea elements named $name inside $html.

  • $html string
  • $name string
  • $class string
    public function _add_control_class($html, $name, $class)
    {
        // a tag with quoted attribute values that may contain '>'
        $tag_re = '~<(input|select|textarea)\b((?:[^>"\']|"[^"]*"|\'[^\']*\')*)>~i';
        $attr_re = '~\s([a-z_:][-a-z0-9_:.]*)(?:\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>"\']+)))?~i';
        return preg_replace_callback($tag_re, function ($m) use ($name, $class, $attr_re) {
            preg_match_all($attr_re, $m[2], $attrs, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
            $found = [];
            foreach ($attrs as $a) {
                $key = strtolower($a[1][0]);
                if ( ! isset($found[$key])) {
                    $value = ($a[2][0] ?? '') !== '' ? $a[2][0] : (($a[3][0] ?? '') !== '' ? $a[3][0] : ($a[4][0] ?? ''));
                    $found[$key] = ['text' => $a[0][0], 'value' => html_entity_decode($value, ENT_QUOTES)];
                }
            }
            if (($found['name']['value'] ?? null) !== $name || in_array(strtolower($found['type']['value'] ?? ''), ['hidden', 'submit', 'button', 'reset'], true)) {
                return $m[0];
            }
            if (isset($found['class'])) {
                $classes = trim($found['class']['value'] . ' ' . $class);
                $attrs_out = str_replace($found['class']['text'], ' class="' . htmlspecialchars($classes, ENT_QUOTES) . '"', $m[2]);
            } else {
                $attrs_out = ' class="' . $class . '"' . $m[2];
            }
            return '<' . $m[1] . $attrs_out . '>';
        }, (string) $html) ?? $html;
    }

_field_state

public function _field_state($extra, $name)

'errors', 'success' or '' for a field, the same lookup the bs3 row does for its wrapper class.

  • $name string
    public function _field_state($extra, $name)
    {
        $dotted = false !== strpos($name, '[') ? str_replace(['[', ']'], ['.', ''], trim($name, '][')) : null;
        foreach (['errors', 'success'] as $key) {
            if (isset($extra[$key][$name]) || ($dotted !== null && isset($extra[$key][$dotted]))) {
                return $key;
            }
        }
        return '';
    }

_floating

public function _floating($content, $extra, $replace = [], $form = null)

Floating label (`floating` option): the control and then its label inside div.form-floating, and no label column (the controls take the no-label offset, so they line up with the other rows). Without a label to float the row stays as it is.

  • $content string
  • $extra array
    public function _floating($content, $extra, $replace = [], $form = null)
    {
        $no_label = $extra['no_label'] ?? (is_object($form) ? ($form->_params['no_label'] ?? false) : false);
        if ($no_label || ! strlen((string) ($extra['desc'] ?? '')) || ($extra['only_row_start'] ?? null) || ($extra['only_row_end'] ?? null)) {
            return [$content, $extra];
        }
        $label_tip_html = ($extra['label_tip'] ?? null) ? trim(' ' . tip($extra['label_tip'], $replace)) : '';
        // the rendered control's id: a select_box() id is select_box_N, not the field name in $extra['id']
        $for_html = preg_match('~<(?:input|select|textarea)\b[^>]*?\sid="([^"]+)"~', $content, $m) ? ' for="' . $m[1] . '"' : _attrs(['for' => $extra['id'] ?? null], ['for']);
        $content = '<div class="' . $this->CLASS_FLOATING . '">' . $content . PHP_EOL
            . '<label' . $for_html . '>' . t($extra['desc']) . $label_tip_html . '</label>' . PHP_EOL
            . '</div>';
        $extra['no_label'] = 1;
        return [$content, $extra];
    }

Inherited methods

from yf_html5fw_empty: _add_rich_editor(), form_render_out()

from yf_html5fw_bs3: _input_group_items()