YOOtheme Pro教程 - 元素

自定义元素是扩展 YOOtheme Pro 页面生成器(Page Builder)功能的最简单方法。 复制现有元素并自定义其标记和设置,或从头开始创建新的元素。

可以使用子主题或Joomla插件将自定义元素添加到页面构建器。添加元素的最简单方法是使用子主题。这通常用于客户项目(现有网站项目)。为YOOtheme Pro开发第三方扩展时应使用Joomla插件。查看Yootheme官网扩展页面,了解开发人员为YOOtheme Pro开发的第三方插件。


#入门

最简单的入门方法是试用示例元素或查看包含的YOOtheme Pro元素。将元素添加到页面构建器后,它将出现在元素库中的Custom组下。

示例元素

GitHub 上的示例元素演示了如何配置元素、扩展其功能以及使用不同的字段类型。 只需下载并解压缩该元素。 最快的尝试方法是使用子主题。

下载 | 查看Github项目

包含的元素

YOOtheme Provendor/yootheme下的相应模块目录中找到包含的元素(系统自带的元素)。

目录 元素
builder/elements accordion, alert, button, code, column, countdown, description_list, divider, gallery, grid, headline, html, icon, image, layout, list, map, overlay, panel, popover, quotation, row, section, slider, slideshow, social, subnav, switcher, table, text, totop, video
builder-newsletter/elements newsletter
builder-joomla/elements breadcrumbs, module, module_position
builder-wordpress/elements breadcrumbs, module, module_position
builder-joomla-source/elements pagination
builder-wordpress-source/elements comments, pagination

如果希望通过定制现有元素来创建新元素,只需复制其中一个包含的元素并修改它的element.json文件为其指定一个唯一名称。


#文档结构

元素有自己的目录,其中包含配置文件和模板文件。

文件 描述
element.json 定义元素配置、字段和设置。
element.php 使用自定义转换或更新功能扩展元素功能。该文件是可选的,必须通过element.json导入。
templates/template.php 渲染元素布局。 渲染通常被拆分为以template-为前缀的模板部分。
templates/content.php 直接输出元素内容而去除布局标记。 内容保存在Joomla页面中。 它供Joomla搜索使用,并在停用YOOtheme Pro时保留于Joomla的文章内容。
images/icon.svg 元素库中显示的图标
images/iconSmall.svg 页面生成器中显示的图标

#JSON配置

element.json定义了元素的名称、图标、字段以及在页面生成器的编辑界面。确保设置不被现有元素采用的唯一元素名称,例如 my_element。以下示例显示了一个没有任何字段的简单元素配置。


{
    "name": "example",
    "title": "Example",
    "icon": "${url:images/icon.svg}",
    "iconSmall": "${url:images/iconSmall.svg}",
    "element": true,
    "width": 500,
    "templates": {
        "render": "./templates/template.php",
        "content": "./templates/content.php"
    }
}
属性 描述
name 元素的名称,值必须是唯一。
title 页面生成器中显示的元素名称
icon 元素库中使用的图标的路径
iconSmall 页面生成器中使用的图标的路径
element 显示元素库中的元素。
width 编辑元素时定制器侧边栏的宽度
templates 两个所需模板文件的路径

组(group)属性

默认情况下,自定义元素会自动分配在元素库中Custom组下。要为元素创建专用组,请设置group属性。如果元素在不同项目之间共用或在为YOOtheme Pro开发第三方扩展时,建议这样做。


"group": "my company"

#字段(fields)

元素的字段可以在element.json文件的fields对象中定义。只需添加一个字段名称及其字段定义。


"fields": {
    "my_field": {}
}

此外,在页面构建器中编辑元素时,设置默认字段集对象中的所有字段以定义它们的顺序和布局。


"fieldset": {
    "default": {
        "fields": [
            "my_field"
        ]
    }
}

属性

每个字段都由其类型和其他属性定义。 以下属性适用于所有字段类型。

属性 描述
name 字段的名称。 未设置时,从对象属性键推断。
type 设置字段输入类型,默认情况下,它是文本类型。
label 在字段上方显示字段的名称。
description 在字段下方显示字段说明。
attrs 向呈现的字段添加额外的HTML属性。
show 仅当满足特定条件时才显示字段。
enable 仅在满足特定条件时启用字段。

在以下示例中,显示了一个名为 Content 的输入字段。


"fields": {
    "content": {
        "label": "Content",
        "description": "A description text.",
        "attrs": {
            "placeholder": "Enter text"
        }
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content"
        ]
    }
}

在以下示例中,只有在填写content字段时才能选择样式选项。 只有填写了content字段并且style设置为primary时,才会显示图标选择器。


"fields": {
    "content": {
        "label": "Content"
    },
    "style": {
        "label": "Style",
        "type": "select",
        "options": {
            "None": "",
            "Primary": "primary",
            "Secondary": "secondary"
        },
        "enable": "content"
    },
    "icon": {
        "label": "Icon",
        "type": "icon",
        "show": "content && style=='primary'"
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content",
            "style",
            "icon"
        ]
    }
}

选项卡(tabs)

这是可选项。将fieldset对象类型设置为tabs,使字段以选项卡的形式呈现。 以下示例有两个选项卡 - ContentSettings。 选项卡中的Content显示内容字段,选项卡中的Settings显示两个选项字段。


"fields": {
    "content": {
        "label": "Content"
    },
    "option_a": {
        "label": "Select",
        "type": "select",
        "options": {
            "Option 1": 0,
            "Option 2": 1,
            "Option 3": 2
        }
    },
    "option_b": {
        "label": "Checkbox",
        "type": "checkbox",
        "text": "Some text"
    }
},
"fieldset": {
    "default": {
        "type": "tabs",
        "fields": [
            {
                "title": "Content",
                "fields": [
                    "content"
                ]
            },
            {
                "title": "Settings",
                "fields": [
                    "option_a",
                    "option_b"
                ]
            }
        ]
    }
}

默认值

将元素添加到页面生成器时,可以将字段设置为默认值。只需在element.json文件中的defaults对象中将字段键设置为默认值。在以下示例中,文本字段content将使用Some default value填充。


"defaults": {
    "content": "Some default value."
},
"fields": {
    "content": {
        "label": "Content",
        "type": "textarea",
        "attrs": {
            "rows": 6
        }
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content"
        ]
    }
}

预览占位符(Preview Placeholder)

字段即使没有内容,也可以在页面生成器的预览区域中显示占位符。填写完字段内容后,占位符消失,并显示字段值。只需修改element.json文件中的placeholder下的props并设置其字段值。在以下示例中,只要未填写内容字段,该元素就会显示“Lorem ipsum ...”占位符文本。


"placeholder": {
    "props": {
        "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
    }
},
"fields": {
    "content": {
        "label": "Content",
        "type": "textarea",
        "attrs": {
            "rows": 6
        }
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content"
        ]
    }
}

插值语法

YOOtheme Pro带有强大的插值语法来引用值和调用函数。这些插值嵌入在字符串中并包裹在${}中,例如${var.foo}

例如,这用于一般元素设置,这些设置通常在元素之间是相同的。这些字段在vendor/yootheme/builder/config/builder.json文件中定义。它们的字段定义可以引用为${builder.NAME}。以下示例将字段名称maxwidth设置为builder.json文件中定义的字段定义。


"fields": {
    "maxwidth": "${builder.maxwidth}"
},
"fieldset": {
    "default": {
        "fields": [
            "maxwidth"
        ]
    }
}

这是builder.json文件中引用的字段定义。


"maxwidth": {
    "label": "Max Width",
    "description": "Set the maximum content width.",
    "type": "select",
    "options": {
        "None": "",
        "Small": "small",
        "Medium": "medium",
        "Large": "large",
        "X-Large": "xlarge",
        "2X-Large": "2xlarge"
    }
}

另一个示例是高级设置选项卡,它在所有元素中也是相同的。 这些字段的顺序和布局也从builder.json文件中引用。


"fields": {
    "content": {
        "label": "Content",
        "type": "textarea"
    },
    "name": "${builder.name}",
    "source": "${builder.source}",
    "id": "${builder.id}",
    "status": "${builder.status}",
    "class": "${builder.cls}",
    "attributes": "${builder.attrs}",
    "css": {
        "type": "editor",
        "label": "CSS",
        "description": "Enter your own custom CSS.",
        "editor": "code",
        "mode": "css",
        "attrs": {
            "debounce": 500
        }
    }
},
"fieldset": {
    "default": {
        "type": "tabs",
        "fields": [
            {
                "title": "Content",
                "fields": [
                    "content"
                ]
            },
            "${builder.advanced}"
        ]
    }
}

这是builder.json文件中的引用值。


"advanced": {
    "title": "Advanced",
    "fields": [
        "name",
        "status",
        "source",
        "id",
        "class",
        "attributes",
        "css"
    ]
}

动态内容(Dynamic Content)

要添加允许选择内容源的dynamic content字段,请从builder.json文件中引用source字段。 如上例所示,引用高级设置选项卡也定义了内容源字段(source field)。


"fields": {
    "source": "${builder.source}"
},
"fieldset": {
    "default": {
        "fields": [
            "source"
        ]
    }
}

要允许字段映射动态内容,请将字段source属性设置为true


"fields": {
    "content": {
        "label": "Content",
        "description": "A text field that can be mapped to a field of a content source.",
        "source": true
    }
}

#字段类型(Field Types)

这是YOOtheme Pro中所有可用内容字段类型(Field Types)的列表。

名称 描述
checkbox 定义一个复选框。
color 定义一个颜色选择器。
editor 定义一个可视化和代码编辑器。
font 定义一个字体选择器。
icon 为UIkit图标库定义一个图标选择器。
image 为媒体库中的文件定义一个图像选择器。
link 为Joomla系统链接和媒体库文件定义链接选择器。
location 定义交互式地图来选择位置。
number 定义一个数字输入字段。
radio 定义一组单选按钮。
range 定义一个带有附加输入字段的范围滑块。
select 定义一个选择框。
text 定义单行文本输入字段。
textarea 为多行文本定义一个纯文本区域。
video 为媒体库中的文件定义视频选择器。

具有附加属性的字段类型如下所述。

复选框字段(Checkbox Field)

复选框字段有一个附加属性 text 来设置复选框旁边的文本。


{
    "label": "Checkbox",
    "type": "checkbox",
    "text": "The text next to the checkbox."
}

编辑器字段有一个editor属性,当设置为 code 时仅加载代码编辑器。 附加的mode属性明确定义了代码语言cssjstext/html

以下示例显示了一个带有VisualCode选项卡的编辑器。


{
    "label": "Editor",
    "type": "editor"
}

以下示例仅显示了带有 CSS 语法突出显示的代码编辑器。


{
    "label": "Code Editor",
    "type": "editor",
    "editor": "code",
    "mode": "css",
    "attrs": {
        "debounce": 500
    }
}

注意 要防止在编辑器中输入时更新定制器预览,请将debounce属性设置为大约500毫秒。

选择字段(Select Field)

选择字段的选项和默认值具有optionsdefault属性。


{
    "label": "Select",
    "type": "select",
    "default": 0,
    "options": {
        "Option 1": 0,
        "Option 2": 1,
        "Option 3": 2
    }
}

单选字段(Radio Field)

单选字段具有nameoptionsdefault属性,分别设置名称、选项和默认选项的值。


{
    "label": "Radio",
    "type": "radio",
    "name": "radio_group",
    "default": 0,
    "options": {
        "Option 1": 0,
        "Option 2": 1,
        "Option 3": 2
    }
}

范围滑块字段(Range Field)

范围滑块字段(Range Field)没有额外的属性,但需要设置minmaxstep这几项HTML属性。


{
    "label": "Range",
    "type": "range",
    "attrs": {
        "min": 1,
        "max": 10,
        "step": 0.5
    }
}

#字段布局(Field Layouts)

某些字段类型仅用于在页面构建器中对字段进行布局,本身没有内容。 它们通常用在fieldset对象中。 包含字段在fields对象中定义。

名称 描述
grid 在网格中排列共享下面的描述文本的字段。
group 使用标签和控件并排紧凑地排列字段,并将描述文本作为工具提示。

网格字段(Grid Field)

网格字段有一个width属性来定义每个网格单元格的宽度。


"fields": {
    "image": {
        "label": "Image",
        "type": "image"
    },
    "width": {
        "label": "Width"
    },
    "height": {
        "label": "Height"
    }
},
"fieldset": {
    "default": {
        "fields": [
            "image",
            {
                "description": "A description text below the grid.",
                "type": "grid",
                "name": "_image_dimension",
                "width": "1-2",
                "fields": [
                    "width",
                    "height"
                ]
            }
        ]
    }
}

注意 fieldset对象中的内联字段定义需要唯一的名称。 默认情况下,label属性用作名称的后备。 但是如果字段定义没有上例中的标签,则必须设置唯一的name。 我们用 _ 前缀标记未在任何地方使用的名称。

组字段(Group Field)

组字段(Group Field)有一个divider属性,用来在组的底部设置一个分隔符。


"fields": {
    "content": {
        "label": "Content"
    },
    "option_a": {
        "label": "Select",
        "type": "select",
        "options": {
            "Option 1": 0,
            "Option 2": 1,
            "Option 3": 2
        }
    },
    "option_b": {
        "label": "Checkbox",
        "type": "checkbox",
        "text": "Some text"
    },
    "option_c": {
        "label": "Text"
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content",
            {
                "label": "Group 1",
                "type": "group",
                "divider": true,
                "fields": [
                    "option_a",
                    "option_b"
                ]
            },
            {
                "label": "Group 2",
                "type": "group",
                "fields": [
                    "option_a",
                    "option_b"
                ]
            }
        ]
    }
}

注意 与网格字段不同,组字段通常有一个label属性,这就是为什么不需要设置name的原因。

#模板文件

在模板中渲染元素节点时,可以使用以下变量。

名称 描述
$node 元素节点(stdClass)
$props 元素属性$node->props使用字段(array)设置
$children $children子元素$node->children,例如包含多个item元素的items的(array)
$builder 当前用于渲染子节点的生成器实例 (YOOtheme\Builder)

字段属性(Field Properties)

veelement.json文件中定义的所有元素字段都可以使用$props变量作为属性进行访问。 它们的类型由字段类型定义,如果用户尚未输入值,则为null


<?php

    // Properties
    $props['option_a'];    // String
    $props['option_b'];    // Integer
    $props['option_c'];    // Boolean

?>

<?php if ($props['title']) : ?>
<h3><?= $props['title'] ?></h3>
<?php endif ?>

<?php if ($props['content']) : ?>
<div><?= $props['content'] ?></div>
<?php endif ?>

模板零件

渲染元素布局通常使用辅助函数$this->render()拆分为以template-为前缀的模板零件。 以下示例呈现template-content.php文件并传递元素属性$props


<?= $this->render("{$__dir}/template-content", compact('props')) ?>

模板引擎

YOOtheme Pro模板引擎提供了一个HTML帮助函数$this->el()来使用$props变量的紧凑插值语法创建 HTML元素。 它还可以轻松合并属性。

语法 描述
foo-{bar} 如果$props['bar']有值,则添加foo-{bar}并将{bar}替换为该值。
foo {@bar} 如果$props['bar']有值,则添加foo
foo {@!bar} 如果$props['bar']没有值,则添加foo
foo {@bar: value} 如果$props['bar']设置为value,则添加foo
foo {@bar: value1|value2} 如果$props['bar']设置为value1value2,则添加foo
foobar [foo {@bar}] 添加foobar并可选择添加foo如果$props['bar']有值。

<?php

//创建包裹元素容器
$el = $this->el('div', [
    'class' => [
        //如果样式有值,则添加两个类
        'uk-card uk-card-{style}',
        //如果样式没有值,则添加一个类
        'uk-panel {@!style}',
        //如果style有值则添加两个类,如果size也有值则添加另一个类
        'uk-card uk-card-{style} [uk-card-{size}]',
    ],
    'style' => [
        //如果min-height有值则为其赋值
        'min-height: {min_height}px',
    ],
    // 添加HTML属性`uk-grid`
    'uk-grid' => true,
]);

// 创建包裹内容容器
$content = $this->el('div', [

    'class' => [
        //如果样式有值则为其添加对应类
        'uk-card-body {@style}',
        // 如果边距大小有值,则添加类似`uk-margin-small`的大小类,否则仅添加`uk-margin`
        'uk-margin[-{margin_size}]',
    ],

]);

?>

<?= $el($props, $attrs) // 开始渲染HTML标签 ?>

    <?php if ($props['content']) : ?>
    <?= $content($props, $props['content']) // 渲染整个HTML的元素 ?>
    <?php endif ?>

<?= $el->end() // 结束渲染HTML标签 ?>

有几个参数传递给 HTML 元素渲染。

实参(ARGUMENT) 类型 描述
$params array 传递所需的元素属性$props
$attrs array (可选)传递其他属性以合并它们。 常规和高级元素设置所需的属性存储在attrs变量中,并应传递给包裹在HTML代码的元素。
$contents mixed 可选地,传递任何内容,例如 $props['content'],整个HTML元素将被渲染。

#转换和更新

可选的element.php文件通过自定义转换或更新函数扩展元素功能。 它必须通过 element.json 文件引入。


{
    "@import": "./element.php",
    "name": "example",
    "title": "Example",
    "icon": "${url:images/icon.svg}",
    "iconSmall": "${url:images/iconSmall.svg}",
    "element": true,
    "width": 500,
    "templates": {
        "render": "./templates/template.php",
        "content": "./templates/content.php"
    }
}

这是一个关于如何为元素节点定义转换和更新的示例。 它还显示可用的对象和参数。


<?php

return [

    // Define transforms for the element node
    'transforms' => [

        // The function is executed before the template is rendered
        'render' => function ($node, array $params) {

            // Element object (stdClass)
            $node->type; // Type name (string)
            $node->props; // Field properties (array)
            $node->children; // All children (array)

            // Parameter array
            $params['path']; // All parent elements (array)
            $params['parent']; // Parent element (stdClass)
            $params['builder']; // Builder instance (YOOtheme\Builder)
            $params['type']; // Element definition (YOOtheme\Builder\ElementType)

        },

    ],

    // Define updates for the element node
    'updates' => [

        // 如果保存元素时的YOOtheme Pro版本小于当前版本,则执行该函数。
        '1.18.0' => function ($node, array $params) {

        },

    ],

];

折叠布局

如果元素的内容字段为空,为了防止元素呈现,采取联动的折叠布局。


return [

    'transforms' => [

        'render' => function ($node) {

            // Don't render the element if the title or content field is empty
            return $node->props['title'] || $node->props['content'];

        },

    ],

];

请注意,对于单个内容字段,返回值必须转换为布尔值。


return [

    'transforms' => [

        'render' => function ($node) {

            // Don't render element if the content field is empty
            return (bool) $node->props['content'];

        },

    ],

];

更新

为新版本的 YOOtheme Pro 定义元素更新。


return [

    'updates' => [

        '2.1.0-beta.1' => function ($node) {

            // Rename a field value
            if (@$node->props['width'] === 'xxlarge') {
                $node->props['width'] = '2xlarge';
            }

        },

        '1.20.2' => function ($node) {

             // Rename a field key
            if (isset($node->props['breakpoint'])) {
                $node->props['grid_breakpoint'] = $node->props['breakpoint'];
                unset($node->props['breakpoint']);
            }

        },

    ],

];

注意 当前元素更新与 YOOtheme Pro 的更新相关联。 它们没有自己的版本号,只能在 YOOtheme Pro 版本号更改时更新。


内容项目

具有内容项的元素,如Grid元素,是包含子元素的父元素。要创建父元素,请将element.json文件中的container属性设置为 true。 要添加一个显示管理content items界面的字段,请使用content-items字段类型并将item属性设置为子元素的name


{
    "name": "example",
    "title": "Example",
    "icon": "${url:images/icon.svg}",
    "iconSmall": "${url:images/iconSmall.svg}",
    "element": true,
    "container": true,
    "width": 500,
    "templates": {
        "render": "./templates/template.php",
        "content": "./templates/content.php"
    },
    "fields": {
        "content": {
            "label": "Items",
            "type": "content-items",
            "item": "example_item"
        }
    },
    "fieldset": {
        "default": {
            "fields": [
                "content"
            ]
        }
    }
}

就像任何其他元素一样,子元素具有自己的目录、具有唯一元素名称和模板文件的 JSON 配置。 它没有显示在元素库中,这就是它也没有图标的原因。 通常,子元素还具有一个高级设置选项卡,该选项卡在所有子元素中都是相同的。 它的字段 order 和 layout 是从builder.json文件中引用的。


{
    "name": "example_item",
    "title": "Item",
    "width": 500,
    "templates": {
        "render": "./templates/template.php",
        "content": "./templates/content.php"
    },
    "fields": {
        "title": {
            "label": "Title",
            "source": true
        },
        "content": {
            "label": "Content",
            "type": "editor",
            "source": true
        },
        "image": {
            "label": "Image",
            "type": "image",
            "source": true
        },
        "status": "${builder.statusItem}",
        "source": "${builder.source}"
    },
    "fieldset": {
        "default": {
            "type": "tabs",
            "fields": [
                {
                    "title": "Content",
                    "fields": [
                        "title",
                        "content",
                        "image"
                    ]
                },
                "${builder.advancedItem}"
            ]
        }
    }
}

子元素应该至少有一个title字段和一个可选的image字段。 两者都将显示在由父元素中的content-items字段创建的内容项列表中。

模板文件

所有子元素都可以通过 $children 数组访问并使用辅助函数 $builder->render() 进行渲染。


<?php foreach ($children as $child) : ?>
<?= $builder->render($child, ['element' => $props]) ?>
<?php endforeach ?>

通常,父元素的$props变量作为$element传递给子元素,因此父元素的所有字段都可以访问模板文件中的子元素。


<?php

    // Property of the parent element
    $element['option_a'];

?>

添加媒体按钮

默认情况下,content-items字段会显示一个AddItem按钮来创建新的内容项。或者,添加一个AddMedia按钮,该按钮允许在媒体管理器中选择图像。对于每个选定的图像,都会创建一个新项目。imagetitle字段会自动填写。

只需定义media对象并将其type属性设置为image。使用item对象定义图像titlesrc设置到哪些字段。


"fields": {
    "content": {
        "label": "Items",
        "type": "content-items",
        "item": "example_item",
        "media": {
            "type": "image",
            "item": {"title": "title", "image": "src"}
        }
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content"
        ]
    }
}

预览占位符

元素可以在页面构建器预览中显示占位符项目,而它们没有内容项目。 添加内容项后,placeholder项将消失,并显示内容项。 只需在element.json文件中的children下的占位符对象中设置占位符项。通过将type属性设置为子元素的name来为每个占位符项创建一个对象。


"placeholder": {
    "children": [
        {"type": "example_item", "props": {}},
        {"type": "example_item", "props": {}},
        {"type": "example_item", "props": {}}
    ]
},
"fields": {
    "content": {
        "label": "Items",
        "type": "content-items",
        "item": "example_item"
    }
},
"fieldset": {
    "default": {
        "fields": [
            "content"
        ]
    }
}

请确保在子元素中定义占位符。


"placeholder": {
    "props": {
        "title": "Title",
        "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
    }
}

如有需要,也可以在props对象中为占位符项的字段设置不同的占位符值。


"placeholder": {
    "children": [
        {"type": "example_item", "props": {"position_x": 20, "position_y": 50}},
        {"type": "example_item", "props": {"position_x": 50, "position_y": 20}},
        {"type": "example_item", "props": {"position_x": 70, "position_y": 70}}
    ]
}