Merge pull request 'Worktree/slider media combo parity' (#28) from worktree/slider-media-combo-parity into main
Reviewed-on: #28
This commit was merged in pull request #28.
This commit is contained in:
@@ -1,311 +0,0 @@
|
|||||||
.widget_ui_slider_box > img {
|
|
||||||
width: calc(60% - 6px);
|
|
||||||
margin: 3px;
|
|
||||||
}
|
|
||||||
.widget_ui_slider_g {
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
.ashu_upload_button,
|
|
||||||
.delimg_upload_button {
|
|
||||||
margin: 3px !important;
|
|
||||||
}
|
|
||||||
.ok_button {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.hide {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.panel-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.panel {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.panel .panel-title {
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px;
|
|
||||||
background: #f4f5f6;
|
|
||||||
}
|
|
||||||
.panel .panel-conter {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
.panel .panel-title:before {
|
|
||||||
content: '\f140';
|
|
||||||
font: normal 20px/1 dashicons;
|
|
||||||
margin: -2px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
/**---------------侧栏小工具:按栏导出/导入(工具条 + 弹层)-----------**/
|
|
||||||
.aksrt-wie-toolbar {
|
|
||||||
margin: 8px 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-toolbar .button {
|
|
||||||
margin-right: 6px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 1000100;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal.is-open {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__backdrop {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.45);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__dialog {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
width: 100%;
|
|
||||||
max-width: min(920px, 96vw);
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow: auto;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #c3c4c7;
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.14);
|
|
||||||
padding: 18px 20px 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__close {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
width: 2em;
|
|
||||||
height: 2em;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 1;
|
|
||||||
color: #646970;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__close:hover {
|
|
||||||
color: #1d2327;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__title {
|
|
||||||
margin: 0 32px 10px 0;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__hint {
|
|
||||||
margin: 0 0 10px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__import-mode {
|
|
||||||
margin: 0 0 12px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: #f6f7f7;
|
|
||||||
border: 1px solid #dcdcde;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 16px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__import-mode-title {
|
|
||||||
margin: 0 0 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__import-mode-row {
|
|
||||||
display: block;
|
|
||||||
margin: 6px 0 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__import-mode-row input {
|
|
||||||
margin-right: 6px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__textarea {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 240px;
|
|
||||||
font-family: Consolas, Monaco, monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.45;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__actions {
|
|
||||||
margin: 12px 0 0;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__result {
|
|
||||||
margin: 10px 0 0;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__result.is-ok {
|
|
||||||
color: #008a20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__result.is-warn {
|
|
||||||
color: #996800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__result.is-err {
|
|
||||||
color: #d63638;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-modal__reload-wrap {
|
|
||||||
margin: 12px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-layer {
|
|
||||||
z-index: 1000120;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-list-wrap {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-list {
|
|
||||||
max-height: 42vh;
|
|
||||||
overflow: auto;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item {
|
|
||||||
flex: 1 1 30%;
|
|
||||||
min-width: 360px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 8px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-label {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
min-width: 0;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: #fbfbfb;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #efeeee;
|
|
||||||
height: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-label:hover {
|
|
||||||
background: #f7fbfe;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-label input[type='radio'] {
|
|
||||||
margin-top: 3px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-expand {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-top: 0 !important;
|
|
||||||
line-height: 1;
|
|
||||||
min-height: 28px;
|
|
||||||
padding-left: 8px !important;
|
|
||||||
padding-right: 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-expand-icon {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-expand-icon::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-top: 5px solid transparent;
|
|
||||||
border-bottom: 5px solid transparent;
|
|
||||||
border-left: 6px solid #50575e;
|
|
||||||
transition: transform 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-expand.is-expanded .aksrt-wie-template-expand-icon::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-modules {
|
|
||||||
padding: 0 12px 12px 40px;
|
|
||||||
margin: 0;
|
|
||||||
background: #f6f7f7;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-module-list {
|
|
||||||
margin: 8px 0 0;
|
|
||||||
padding-left: 1.2em;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-module-list li {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-module-id {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #646970;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-remind {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #fc844c;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aksrt-wie-template-item-desc {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #646970;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
.widget_ui_slider_box>img{width:calc(60% - 6px);margin:3px}.widget_ui_slider_g{padding:5px 0}.ashu_upload_button,.delimg_upload_button{margin:3px!important}.ok_button{float:right}.hide{display:none!important}.panel-hide{display:none}.panel{border:1px solid #ddd;display:block}.panel .panel-title{margin:0;padding:10px;background:#f4f5f6}.panel .panel-conter{padding:6px}.panel .panel-title:before{content:'\f140';font:normal 20px/1 dashicons;margin:-2px;float:right}.aksrt-wie-toolbar{margin:8px 0 12px}.aksrt-wie-toolbar .button{margin-right:6px;margin-bottom:4px}.aksrt-wie-modal{display:none;position:fixed;inset:0;z-index:1000100;align-items:center;justify-content:center;padding:20px;box-sizing:border-box}.aksrt-wie-modal.is-open{display:flex}.aksrt-wie-modal__backdrop{position:absolute;inset:0;background:rgba(0,0,0,.45)}.aksrt-wie-modal__dialog{position:relative;z-index:1;width:100%;max-width:min(920px,96vw);max-height:90vh;overflow:auto;background:#fff;border-radius:6px;border:1px solid #c3c4c7;box-shadow:0 8px 32px rgba(0,0,0,.14);padding:18px 20px 16px;box-sizing:border-box}.aksrt-wie-modal__close{position:absolute;top:10px;right:10px;width:2em;height:2em;padding:0;border:none;background:0 0;cursor:pointer;font-size:20px;line-height:1;color:#646970}.aksrt-wie-modal__close:hover{color:#1d2327}.aksrt-wie-modal__title{margin:0 32px 10px 0;font-size:16px;line-height:1.3}.aksrt-wie-modal__hint{margin:0 0 10px;font-size:13px}.aksrt-wie-modal__import-mode{margin:0 0 12px;padding:10px 12px;background:#f6f7f7;border:1px solid #dcdcde;border-radius:4px;display:flex;align-items:baseline;gap:16px;flex-wrap:wrap}.aksrt-wie-modal__import-mode-title{margin:0 0 8px;font-weight:600}.aksrt-wie-modal__import-mode-row{display:block;margin:6px 0 0;cursor:pointer}.aksrt-wie-modal__import-mode-row input{margin-right:6px;vertical-align:middle}.aksrt-wie-modal__textarea{width:100%;min-height:240px;font-family:Consolas,Monaco,monospace;font-size:12px;line-height:1.45;box-sizing:border-box}.aksrt-wie-modal__actions{margin:12px 0 0;display:flex;flex-wrap:wrap;gap:8px;align-items:center}.aksrt-wie-modal__result{margin:10px 0 0;font-size:13px}.aksrt-wie-modal__result.is-ok{color:#008a20}.aksrt-wie-modal__result.is-warn{color:#996800}.aksrt-wie-modal__result.is-err{color:#d63638}.aksrt-wie-modal__reload-wrap{margin:12px 0 0}.aksrt-wie-template-layer{z-index:1000120}.aksrt-wie-template-list-wrap{margin-bottom:12px}.aksrt-wie-template-list{max-height:42vh;overflow:auto;display:flex;gap:10px;flex-wrap:wrap}.aksrt-wie-template-item{flex:1 1 30%;min-width:360px}.aksrt-wie-template-item:last-child{border-bottom:none}.aksrt-wie-template-item-row{display:flex;align-items:flex-start;gap:8px;height:100%}.aksrt-wie-template-item-label{display:flex;flex:1 1 auto;gap:10px;align-items:flex-start;margin:0;cursor:pointer;min-width:0;padding:10px 12px;background:#fbfbfb;border-radius:8px;border:1px solid #efeeee;height:stretch}.aksrt-wie-template-item-label:hover{background:#f7fbfe}.aksrt-wie-template-item-label input[type=radio]{margin-top:3px;flex-shrink:0}.aksrt-wie-template-item-expand{flex-shrink:0;margin-top:0!important;line-height:1;min-height:28px;padding-left:8px!important;padding-right:8px!important}.aksrt-wie-template-expand-icon{display:inline-block;vertical-align:middle}.aksrt-wie-template-expand-icon::before{content:'';display:block;width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:6px solid #50575e;transition:transform .15s ease}.aksrt-wie-template-item-expand.is-expanded .aksrt-wie-template-expand-icon::before{transform:rotate(90deg)}.aksrt-wie-template-item-modules{padding:0 12px 12px 40px;margin:0;background:#f6f7f7;border-top:1px solid #eee}.aksrt-wie-template-module-list{margin:8px 0 0;padding-left:1.2em;font-size:13px;line-height:1.5}.aksrt-wie-template-module-list li{margin-bottom:4px}.aksrt-wie-template-module-id{font-size:11px;color:#646970;margin-left:4px}.aksrt-wie-template-item-meta{display:flex;flex-direction:column;gap:4px}.aksrt-wie-template-item-title{font-weight:600}.aksrt-wie-template-item-remind{font-size:12px;color:#fc844c;line-height:1.4}.aksrt-wie-template-item-desc{font-size:12px;color:#646970;line-height:1.4}
|
|
||||||
@@ -165,6 +165,7 @@
|
|||||||
frame.on('select', () => {
|
frame.on('select', () => {
|
||||||
const attachment = frame.state().get('selection').first().toJSON();
|
const attachment = frame.state().get('selection').first().toJSON();
|
||||||
$wrap.find('input[type="url"]').val(attachment.url).trigger('change');
|
$wrap.find('input[type="url"]').val(attachment.url).trigger('change');
|
||||||
|
if ($wrap.data('media-library') === 'video') return;
|
||||||
let $image = $wrap.find('img');
|
let $image = $wrap.find('img');
|
||||||
if (!$image.length) $image = $('<img alt="">').prependTo($wrap);
|
if (!$image.length) $image = $('<img alt="">').prependTo($wrap);
|
||||||
$image.attr('src', attachment.url);
|
$image.attr('src', attachment.url);
|
||||||
|
|||||||
@@ -193,7 +193,8 @@ final class FormRenderer {
|
|||||||
private static function upload(string $id, string $name, string $value, string $library = ''): void {
|
private static function upload(string $id, string $name, string $value, string $library = ''): void {
|
||||||
$library = in_array($library, ['image', 'video'], true) ? $library : '';
|
$library = in_array($library, ['image', 'video'], true) ? $library : '';
|
||||||
echo '<div class="aksrt-widget-upload" data-media-library="' . esc_attr($library) . '">';
|
echo '<div class="aksrt-widget-upload" data-media-library="' . esc_attr($library) . '">';
|
||||||
if ($value !== '') {
|
// 参考主题:视频上传字段不展示预览图(视频 URL 作为 img src 会得到破图)。
|
||||||
|
if ($value !== '' && $library !== 'video') {
|
||||||
echo '<img src="' . esc_url($value) . '" alt="">';
|
echo '<img src="' . esc_url($value) . '" alt="">';
|
||||||
}
|
}
|
||||||
echo '<input class="widefat" type="url" id="' . esc_attr($id) . '" name="' . esc_attr($name) . '" value="' . esc_attr($value) . '">';
|
echo '<input class="widefat" type="url" id="' . esc_attr($id) . '" name="' . esc_attr($name) . '" value="' . esc_attr($value) . '">';
|
||||||
@@ -240,19 +241,20 @@ final class FormRenderer {
|
|||||||
|
|
||||||
private static function group(SchemaWidget $widget, array $field, array $rows, string $key): void {
|
private static function group(SchemaWidget $widget, array $field, array $rows, string $key): void {
|
||||||
$token = '__index_' . substr(md5($key), 0, 8) . '__';
|
$token = '__index_' . substr(md5($key), 0, 8) . '__';
|
||||||
|
$button_title = (string) ($field['button_title'] ?? __('添加项目', 'aksrt'));
|
||||||
echo '<div class="aksrt-widget-repeater" data-index-token="' . esc_attr($token) . '" data-max-items="' . esc_attr((string) ($field['max_items'] ?? 50)) . '"><div class="aksrt-widget-repeater__rows">';
|
echo '<div class="aksrt-widget-repeater" data-index-token="' . esc_attr($token) . '" data-max-items="' . esc_attr((string) ($field['max_items'] ?? 50)) . '"><div class="aksrt-widget-repeater__rows">';
|
||||||
foreach ($rows as $index => $row) {
|
foreach ($rows as $index => $row) {
|
||||||
self::group_row($widget, $field, is_array($row) ? $row : [], $key, (string) $index);
|
self::group_row($widget, $field, is_array($row) ? $row : [], $key, (string) $index);
|
||||||
}
|
}
|
||||||
echo '</div><template class="aksrt-widget-repeater__template">';
|
echo '</div><template class="aksrt-widget-repeater__template">';
|
||||||
self::group_row($widget, $field, [], $key, $token);
|
self::group_row($widget, $field, [], $key, $token);
|
||||||
echo '</template><button type="button" class="button aksrt-widget-repeater__add">' . esc_html__('添加项目', 'aksrt') . '</button></div>';
|
echo '</template><button type="button" class="button aksrt-widget-repeater__add">' . esc_html($button_title) . '</button></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function group_row(SchemaWidget $widget, array $field, array $row, string $key, string $index): void {
|
private static function group_row(SchemaWidget $widget, array $field, array $row, string $key, string $index): void {
|
||||||
$prefix = (string) ($field['item_label'] ?? $field['label'] ?? __('项目', 'aksrt'));
|
$prefix = (string) ($field['item_label'] ?? $field['label'] ?? __('项目', 'aksrt'));
|
||||||
$number = ctype_digit($index) ? ((int) $index + 1) : 0;
|
$number = ctype_digit($index) ? ((int) $index + 1) : 0;
|
||||||
$title = $prefix . ($number ? ' ' . $number : '');
|
$title = $prefix . $number;
|
||||||
if (!empty($row['title'])) $title .= ':' . strip_tags((string) $row['title']);
|
if (!empty($row['title'])) $title .= ':' . strip_tags((string) $row['title']);
|
||||||
echo '<div class="aksrt-widget-repeater__row" data-row-index="' . esc_attr(ctype_digit($index) ? $index : '0') . '"><div class="aksrt-widget-repeater__bar"><span class="dashicons dashicons-move" aria-hidden="true"></span><strong>' . esc_html($title) . '</strong><button type="button" class="button-link-delete aksrt-widget-repeater__remove">' . esc_html__('删除', 'aksrt') . '</button></div><div class="aksrt-widget-repeater__fields">';
|
echo '<div class="aksrt-widget-repeater__row" data-row-index="' . esc_attr(ctype_digit($index) ? $index : '0') . '"><div class="aksrt-widget-repeater__bar"><span class="dashicons dashicons-move" aria-hidden="true"></span><strong>' . esc_html($title) . '</strong><button type="button" class="button-link-delete aksrt-widget-repeater__remove">' . esc_html__('删除', 'aksrt') . '</button></div><div class="aksrt-widget-repeater__fields">';
|
||||||
foreach ((array) ($field['fields'] ?? []) as $child) {
|
foreach ((array) ($field['fields'] ?? []) as $child) {
|
||||||
|
|||||||
@@ -10,101 +10,179 @@ defined('ABSPATH') || exit;
|
|||||||
final class SliderRenderer {
|
final class SliderRenderer {
|
||||||
public static function render(string $slug, array $instance, array $context = []): string {
|
public static function render(string $slug, array $instance, array $context = []): string {
|
||||||
unset($context);
|
unset($context);
|
||||||
return $slug === 'slider_media_combo' ? self::combo($instance) : self::slider((array) ($instance['slides'] ?? []), (array) ($instance['option'] ?? []), true);
|
if ($slug === 'slider_media_combo') {
|
||||||
|
return self::combo($instance);
|
||||||
|
}
|
||||||
|
$slides = (array) ($instance['slides'] ?? []);
|
||||||
|
if (!$slides) {
|
||||||
|
// 参考主题幻灯片(新):无可显示内容时仅向超级管理员提示。
|
||||||
|
if (function_exists('is_super_admin') && is_super_admin()) {
|
||||||
|
return '<div class="c-red muted-box mb20"><b>[' . esc_html__('幻灯片(新)', 'aksrt') . ']' . esc_html__('幻灯片模块:', 'aksrt') . '</b>' . esc_html__('当前配置下没有可显示的内容,请正确配置或移出该模块', 'aksrt') . '</div>';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return self::slider($slides, (array) ($instance['option'] ?? []), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function slider(array $slides, array $option, bool $with_margin = false): string {
|
private static function slider(array $slides, array $option, bool $with_margin = false): string {
|
||||||
$option = array_merge(['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180, 'pc_height' => 400, 'm_height' => 200, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4], $option);
|
$option = array_merge(['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180, 'pc_height' => 400, 'm_height' => 200, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4], $option);
|
||||||
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
||||||
|
$lazy = aksrt_is_lazy('lazy_sider');
|
||||||
|
$lazy_src = aksrt_get_lazy_thumb('lg');
|
||||||
$items = ''; $count = 0;
|
$items = ''; $count = 0;
|
||||||
foreach ($slides as $slide) {
|
foreach ($slides as $slide) {
|
||||||
if (!is_array($slide)) continue;
|
if (!is_array($slide)) continue;
|
||||||
$hide = (string) ($slide['hide'] ?? '');
|
$hide = (string) ($slide['hide'] ?? '');
|
||||||
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
||||||
$background = esc_url((string) ($slide['background'] ?? '')); $video = esc_url((string) ($slide['background_video'] ?? ''));
|
$slide_class = (string) ($slide['class'] ?? '');
|
||||||
if ($background === '' && ($video === '' || $is_mobile)) continue;
|
$background = (string) ($slide['background'] ?? '');
|
||||||
$alt = (string) (($slide['text']['title'] ?? '') ?: __('幻灯片', 'aksrt'));
|
$video = (string) ($slide['background_video'] ?? '');
|
||||||
$slide_class = '';
|
$media = '';
|
||||||
if ($video !== '' && !$is_mobile) { $slide_class = ' slide-background-video'; $media = '<video autoplay loop muted playsinline class="fit-cover radius8" src="' . $video . '"' . ($background !== '' ? ' poster="' . $background . '"' : '') . '></video><div class="absolute"></div>'; }
|
if ($video !== '' && !$is_mobile) {
|
||||||
else $media = '<img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : $background) . '" data-src="' . $background . '" alt="' . esc_attr($alt) . '">';
|
$slide_class .= ' slide-background-video';
|
||||||
|
$poster = $background !== '' ? ' poster="' . esc_url($background) . '"' : '';
|
||||||
|
$media = '<video autoplay="" loop="" muted="" class="fit-cover radius8' . ($lazy ? ' lazyload' : '') . '"' . ($lazy ? ' data-src="' . esc_url($video) . '" src=""' : ' src="' . esc_url($video) . '"') . $poster . '></video><div class="absolute"></div>';
|
||||||
|
}
|
||||||
|
if ($media === '' && $background !== '') {
|
||||||
|
$alt = (string) (($slide['text']['title'] ?? '') ?: ($slide['link']['text'] ?? '') ?: __('幻灯片', 'aksrt'));
|
||||||
|
$media = '<img class="radius8' . ($lazy ? ' lazyload swiper-lazy' : '') . '" ' . ($lazy ? 'data-src="' . esc_url($background) . '" src="' . esc_url($lazy_src) . '"' : 'src="' . esc_url($background) . '"') . ' alt="' . esc_attr($alt) . '">';
|
||||||
|
}
|
||||||
|
if ($media === '') continue;
|
||||||
$layers = '';
|
$layers = '';
|
||||||
foreach ((array) ($slide['image_layer'] ?? []) as $layer) {
|
foreach ((array) ($slide['image_layer'] ?? []) as $layer) {
|
||||||
if (!is_array($layer) || empty($layer['image'])) continue;
|
if (!is_array($layer) || empty($layer['image'])) continue;
|
||||||
$attrs = ''; $parallax = (int) ($layer['parallax'] ?? 0); $scale = (float) ($layer['parallax_scale'] ?? 100) / 100; $opacity = (float) ($layer['parallax_opacity'] ?? 100) / 100;
|
$parallax = (int) ($layer['parallax'] ?? 0);
|
||||||
if ($parallax) $attrs .= ' data-swiper-parallax="' . $parallax . '%"'; if ($scale !== 1.0) $attrs .= ' data-swiper-parallax-scale="' . esc_attr((string) $scale) . '"'; if ($opacity !== 1.0) $attrs .= ' data-swiper-parallax-opacity="' . esc_attr((string) $opacity) . '"';
|
$parallax_attr = $parallax ? ' data-swiper-parallax="' . $parallax . '%"' : '';
|
||||||
|
$opacity = (float) ($layer['parallax_opacity'] ?? 100) / 100;
|
||||||
|
$parallax_attr .= ($parallax && $opacity && $opacity !== 1.0) ? ' data-swiper-parallax-opacity="' . esc_attr((string) $opacity) . '"' : '';
|
||||||
|
$scale = (float) ($layer['parallax_scale'] ?? 100) / 100;
|
||||||
|
$parallax_attr .= ($scale && $scale !== 1.0) ? ' data-swiper-parallax-scale="' . esc_attr((string) $scale) . '"' : '';
|
||||||
$class = !empty($layer['free_size']) ? ' slide-layer text-' . esc_attr((string) ($layer['align'] ?? 'center')) : '';
|
$class = !empty($layer['free_size']) ? ' slide-layer text-' . esc_attr((string) ($layer['align'] ?? 'center')) : '';
|
||||||
$layers .= '<div class="absolute' . $class . '"' . $attrs . '><img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : (string) $layer['image']) . '" data-src="' . esc_url((string) $layer['image']) . '" alt="' . esc_attr($alt) . '"></div>';
|
$layers .= '<div class="absolute' . $class . '"' . $parallax_attr . '><img class="radius8' . ($lazy ? ' lazyload swiper-lazy' : '') . '" ' . ($lazy ? 'data-src="' . esc_url((string) $layer['image']) . '" src="' . esc_url($lazy_src) . '"' : 'src="' . esc_url((string) $layer['image']) . '"') . ' alt="' . esc_attr((string) (($slide['text']['title'] ?? '') ?: __('幻灯片', 'aksrt'))) . '"></div>';
|
||||||
}
|
}
|
||||||
$text = (array) ($slide['text'] ?? []); $copy = '';
|
$text = (array) ($slide['text'] ?? []); $copy = '';
|
||||||
if (!empty($text['title'])) { $style = ''; if (!empty($text['text_size_pc'])) $style .= '--text-size-pc:' . (int) $text['text_size_pc'] . 'px;'; if (!empty($text['text_size_m'])) $style .= '--text-size-m:' . (int) $text['text_size_m'] . 'px;'; $copy = '<div class="abs-center slide-text ' . esc_attr((string) ($text['text_align'] ?? 'left-bottom')) . '"' . ($style ? ' style="' . esc_attr($style) . '"' : '') . '><div class="slide-title">' . wp_kses_post((string) $text['title']) . '</div>' . (!empty($text['desc']) ? '<div class="slide-desc">' . wp_kses_post((string) $text['desc']) . '</div>' : '') . '</div>'; if (!empty($text['parallax'])) $copy = '<div class="absolute" data-swiper-parallax="' . (int) $text['parallax'] . '">' . $copy . '</div>'; }
|
if (!empty($text['title'])) { $style = ''; if (!empty($text['text_size_pc'])) $style .= '--text-size-pc:' . (int) $text['text_size_pc'] . 'px;'; if (!empty($text['text_size_m'])) $style .= '--text-size-m:' . (int) $text['text_size_m'] . 'px;'; $copy = '<div class="abs-center slide-text ' . esc_attr((string) ($text['text_align'] ?? 'left-bottom')) . '"' . ($style ? ' style="' . esc_attr($style) . '"' : '') . '><div class="slide-title">' . wp_kses_post((string) $text['title']) . '</div>' . (!empty($text['desc']) ? '<div class="slide-desc">' . wp_kses_post((string) $text['desc']) . '</div>' : '') . '</div>'; if (!empty($text['parallax'])) $copy = '<div class="absolute" data-swiper-parallax="' . (int) $text['parallax'] . '">' . $copy . '</div>'; }
|
||||||
$body = $media . $layers . $copy; $link = RendererHelpers::link((array) ($slide['link'] ?? [])); if ($link['url'] !== '') $body = '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . $body . '</a>';
|
$body = $media . $layers . $copy;
|
||||||
$items .= '<div class="swiper-slide' . esc_attr($slide_class) . '">' . $body . '</div>'; $count++;
|
$link = RendererHelpers::link((array) ($slide['link'] ?? []));
|
||||||
|
$body = $link['url'] !== ''
|
||||||
|
? '<a' . ($link['target'] !== '' ? ' target="_blank"' : '') . ' href="' . $link['url'] . '">' . $body . '</a>'
|
||||||
|
: '<span>' . $body . '</span>';
|
||||||
|
$items .= '<div class="swiper-slide' . ($slide_class !== '' ? ' ' . esc_attr(trim($slide_class)) : '') . '">' . $body . '</div>'; $count++;
|
||||||
}
|
}
|
||||||
if (!$count) return RendererHelpers::empty_state(__('请配置幻灯片', 'aksrt'));
|
if (!$count) return '';
|
||||||
$class = 'new-swiper slide-widget' . ($with_margin ? ' mb20' : '') . ($option['scale_height'] && $option['direction'] !== 'vertical' ? ' scale-height' : '') . ($count === 1 ? ' swiper-container-initialized' : ''); $attrs = ' data-direction="' . esc_attr((string) $option['direction']) . '" data-effect="' . esc_attr((string) $option['effect']) . '"';
|
$class = 'new-swiper slide-widget' . ($with_margin ? ' mb20' : '') . ($option['scale_height'] && $option['direction'] !== 'vertical' ? ' scale-height' : '') . ($count === 1 ? ' swiper-container-initialized' : '');
|
||||||
if (!empty($option['loop']) && $count > 1) $attrs .= ' data-loop="true"'; if (!empty($option['autoplay']) && $count > 1) $attrs .= ' data-autoplay="1" data-interval="' . ((int) $option['interval'] * 1000) . '"'; if ((int) $option['speed'] > 0) $attrs .= ' data-speed="' . (int) $option['speed'] . '"'; $attrs .= ' data-spaceBetween="' . (int) $option['spacebetween'] . '"';
|
$attrs = ' data-direction="' . esc_attr((string) $option['direction']) . '" data-effect="' . esc_attr((string) $option['effect']) . '"';
|
||||||
if ($option['auto_height']) $attrs .= ' auto-height="true" style="--max-height:' . (int) $option['max_height'] . 'px;--min-height:' . (int) $option['min_height'] . 'px;"'; else $attrs .= ' style="' . esc_attr($option['scale_height'] && $option['direction'] !== 'vertical' ? '--scale-height:' . (int) $option['scale'] . '%;' : '--pc-height:' . (int) $option['pc_height'] . 'px;--m-height:' . (int) $option['m_height'] . 'px;') . '"';
|
if (!empty($option['loop'])) $attrs .= ' data-loop="true"';
|
||||||
$html = '<div class="' . esc_attr($class) . '"' . $attrs . '><div class="swiper-wrapper">' . $items . '</div>'; if (!empty($option['button']) && $count > 1) $html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>'; if (!empty($option['pagination']) && $count > 1) $html .= '<div class="swiper-pagination kaoyou"></div>'; return '<div class="relative zib-slider">' . $html . '</div>';
|
$interval = (int) $option['interval'];
|
||||||
|
if ($interval < 999) $interval *= 1000;
|
||||||
|
if (!empty($option['autoplay'])) $attrs .= ' data-autoplay="1" data-interval="' . $interval . '"';
|
||||||
|
if ((int) $option['speed'] > 299) $attrs .= ' data-speed="' . (int) $option['speed'] . '"';
|
||||||
|
$attrs .= ' data-spaceBetween="' . (int) $option['spacebetween'] . '"';
|
||||||
|
if ($option['auto_height'] && $option['direction'] !== 'vertical') {
|
||||||
|
$attrs .= ' auto-height="true" style="--max-height:' . (int) $option['max_height'] . 'px;--min-height:' . (int) $option['min_height'] . 'px;"';
|
||||||
|
} elseif ($option['scale_height'] && $option['direction'] !== 'vertical') {
|
||||||
|
$attrs .= ' style="--scale-height:' . (int) $option['scale'] . '%;"';
|
||||||
|
} else {
|
||||||
|
$attrs .= ' style="--pc-height:' . (int) $option['pc_height'] . 'px;--m-height:' . (int) $option['m_height'] . 'px;"';
|
||||||
|
}
|
||||||
|
$html = '<div class="' . esc_attr($class) . '"' . $attrs . '><div class="swiper-wrapper">' . $items . '</div>';
|
||||||
|
if (!empty($option['button']) && $count > 1) $html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
|
||||||
|
if (!empty($option['pagination']) && $count > 1) $html .= '<div class="swiper-pagination kaoyou"></div>';
|
||||||
|
return '<div class="relative zib-slider">' . $html . '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function combo(array $instance): string {
|
private static function combo(array $instance): string {
|
||||||
$option = (array) ($instance['option'] ?? []); $option['direction'] = 'horizontal'; $option['scale_height'] = true; $option['scale'] = (int) ($instance['slide_scale'] ?? 50);
|
$has_slider = !empty($instance['slides'][0]);
|
||||||
$slide_list = (array) ($instance['slides'] ?? []);
|
$has_covers = !empty($instance['covers'][0]['image']) || !empty($instance['covers'][0]['video']);
|
||||||
$slider = !empty($slide_list[0]) ? self::slider($slide_list, $option) : '';
|
// 参考主题 is_show 门控:幻灯片需含背景媒体才视为有内容。
|
||||||
|
$show_slider = $has_slider && (!empty($instance['slides'][0]['background']) || !empty($instance['slides'][0]['background_video']));
|
||||||
|
if (!$show_slider && !$has_covers) return '';
|
||||||
|
|
||||||
|
$slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50));
|
||||||
|
$slider = '';
|
||||||
|
if ($has_slider && is_array($instance['option'])) {
|
||||||
|
$option = $instance['option'];
|
||||||
|
$option['class'] = 'slide-widget';
|
||||||
|
$option['direction'] = 'horizontal';
|
||||||
|
$option['scale_height'] = true;
|
||||||
|
$option['scale'] = $slide_scale;
|
||||||
|
$slider = self::slider((array) $instance['slides'], $option);
|
||||||
|
}
|
||||||
|
|
||||||
|
$m_layout_type = (string) ($instance['m_layout_type'] ?? '');
|
||||||
|
$layout_ratio = max(0, (int) ($instance['layout_ratio'] ?? 60));
|
||||||
|
$cover_row_count = max(1, (int) ($instance['pc_row'] ?? 2));
|
||||||
|
$cover_col_count = max(1, (int) ($instance['pc_col'] ?? 2));
|
||||||
|
$cover_gap = 3;
|
||||||
|
|
||||||
|
// 右侧卡片整体长宽比由左侧幻灯片宽度占比与长宽比推导,gap 与参考主题一致。
|
||||||
|
$cover_base_ratio = $layout_ratio < 100 ? (($layout_ratio * $slide_scale) / (100 - $layout_ratio)) : 0;
|
||||||
|
$cover_ratio = (($cover_base_ratio - ($cover_col_count - 1) * $cover_gap) / $cover_col_count) * $cover_row_count;
|
||||||
|
|
||||||
|
$cover_m_row = max(1, (int) ($instance['m_row'] ?? 2));
|
||||||
|
$m_cover_ratio = (int) ($instance['m_cover_ratio'] ?? 45);
|
||||||
|
if ($m_cover_ratio < 0) $m_cover_ratio = (int) $cover_ratio;
|
||||||
|
if ($m_layout_type === 'default') $cover_m_row = $cover_row_count;
|
||||||
|
|
||||||
|
$style_attr = ' --layout-ratio:' . $layout_ratio . '%; --cover-ratio:' . $cover_ratio . '%; --m-cover-ratio:' . $m_cover_ratio . '%;';
|
||||||
|
|
||||||
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
||||||
$cover_list = array_values((array) ($instance['covers'] ?? []));
|
$cover_list = (array) ($instance['covers'] ?? []);
|
||||||
$is_row = count($cover_list) > 1;
|
$is_row = count($cover_list) > 1;
|
||||||
$layout_type = (string) ($instance['m_layout_type'] ?? 'vertical');
|
$row_class = 'col-sm-' . (int) (12 / $cover_row_count);
|
||||||
$pc_row = max(1, min(4, (int) ($instance['pc_row'] ?? 2)));
|
$row_class .= $cover_m_row > 1 ? ' col-xs-' . (int) (12 / $cover_m_row) : '';
|
||||||
$m_row = $layout_type === 'default' ? $pc_row : max(1, min(6, (int) ($instance['m_row'] ?? 2)));
|
$animation_class = self::animation_class($instance);
|
||||||
$row_class = 'col-sm-' . (int) (12 / $pc_row) . ($m_row > 1 ? ' col-xs-' . (int) (12 / $m_row) : '');
|
|
||||||
$m_cover_ratio = max(0, (int) ($instance['m_cover_ratio'] ?? 45));
|
$font_size_pc = (int) ($instance['font_size_pc'] ?? 16);
|
||||||
$mask = max(0, min(90, (int) ($instance['mask_opacity'] ?? 0))) / 100;
|
$font_size_m = (int) ($instance['font_size_m'] ?? 14);
|
||||||
$cover_style_vars = '--font-size:' . (int) ($instance['font_size_pc'] ?? 16) . 'px;--font-size-sm:' . (int) ($instance['font_size_m'] ?? 14) . 'px;';
|
|
||||||
if (!empty($instance['font_bold'])) $cover_style_vars .= '--font-weight:bold;--font-weight-sm:bold;';
|
|
||||||
$font_color = (string) ($instance['font_color'] ?? '');
|
$font_color = (string) ($instance['font_color'] ?? '');
|
||||||
if ($font_color !== '') $cover_style_vars .= '--color:' . $font_color . ';--color-sm:' . $font_color . ';';
|
$font_style = '';
|
||||||
|
$font_style .= $font_size_pc && 14 !== $font_size_pc ? '--font-size:' . $font_size_pc . 'px;' : '';
|
||||||
|
$font_style .= $font_size_m && 14 !== $font_size_m ? '--font-size-sm:' . $font_size_m . 'px;' : '';
|
||||||
|
$font_style .= !empty($instance['font_bold']) ? '--font-weight:bold;--font-weight-sm:bold;' : '';
|
||||||
|
$font_style .= $font_color !== '' ? '--color:' . $font_color . ';--color-sm:' . $font_color . ';' : '';
|
||||||
|
$font_style_attr = $font_style !== '' ? ' style="' . esc_attr($font_style) . '"' : '';
|
||||||
|
|
||||||
$covers = '';
|
$covers = '';
|
||||||
foreach ($cover_list as $cover) {
|
foreach ($cover_list as $cover) {
|
||||||
if (!is_array($cover)) continue;
|
if (!is_array($cover)) continue;
|
||||||
$hide = (string) ($cover['hide'] ?? '');
|
$hide = (string) ($cover['hide'] ?? '');
|
||||||
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
||||||
$image = esc_url((string) ($cover['image'] ?? ''));
|
|
||||||
$video = esc_url((string) ($cover['video'] ?? ''));
|
|
||||||
$title = (string) ($cover['title'] ?? '');
|
$title = (string) ($cover['title'] ?? '');
|
||||||
$link = RendererHelpers::link((array) ($cover['link'] ?? []));
|
$link = RendererHelpers::link((array) ($cover['link'] ?? []));
|
||||||
$card = aksrt_graphic_card([
|
$card = aksrt_graphic_card([
|
||||||
'class' => 'noshadow mb10',
|
'class' => 'noshadow mb10',
|
||||||
'img' => $image,
|
'img' => (string) ($cover['image'] ?? ''),
|
||||||
'video' => $video,
|
'video' => (string) ($cover['video'] ?? ''),
|
||||||
'alt' => wp_strip_all_tags($title),
|
'alt' => wp_strip_all_tags($title),
|
||||||
'link' => ['url' => $link['url'], 'target' => $link['target']],
|
'link' => ['url' => $link['url'], 'target' => $link['target']],
|
||||||
|
'lazy' => aksrt_is_lazy('lazy_cover'),
|
||||||
'more' => trim($title) !== '' ? '<div class="abs-center text-center graphic-text this-font">' . wp_kses_post($title) . '</div>' : '',
|
'more' => trim($title) !== '' ? '<div class="abs-center text-center graphic-text this-font">' . wp_kses_post($title) . '</div>' : '',
|
||||||
'height_scale' => 0,
|
'height_scale' => 0,
|
||||||
'mask_opacity' => (int) ($instance['mask_opacity'] ?? 0),
|
'mask_opacity' => (int) ($instance['mask_opacity'] ?? 0),
|
||||||
]);
|
]);
|
||||||
if ($card === '') continue;
|
$col_class = trim($animation_class . ($is_row ? ' ' . $row_class : ''));
|
||||||
$covers .= '<div class="' . ($is_row ? esc_attr($row_class) : '') . '">' . $card . '</div>';
|
$covers .= '<div class="' . esc_attr($col_class) . '">' . $card . '</div>';
|
||||||
}
|
}
|
||||||
if ($covers !== '') {
|
$cover_html = '<div class="widget-graphic-cover ' . ($is_row ? 'mb10' : 'mb20') . '"><div' . $font_style_attr . '>' . ($is_row ? '<div class="row gutters-5">' : '') . $covers . ($is_row ? '</div>' : '') . '</div></div>';
|
||||||
$covers = '<div class="widget-graphic-cover" style="' . esc_attr($cover_style_vars) . '">' . ($is_row ? '<div class="row gutters-5">' : '') . $covers . ($is_row ? '</div>' : '') . '</div>';
|
|
||||||
|
$class = 'flex widget-slider-graphic m-' . sanitize_html_class($m_layout_type);
|
||||||
|
$html = '<div class="' . esc_attr($class) . '" style="' . esc_attr($style_attr) . '" data-row="' . $cover_row_count . '" data-col="' . $cover_col_count . '">';
|
||||||
|
if ($slider !== '') {
|
||||||
|
$html .= '<div class="slider-col mb20' . $animation_class . '">' . $slider . '</div>';
|
||||||
|
} elseif (function_exists('is_super_admin') && is_super_admin()) {
|
||||||
|
$html .= '<div class="slider-col mb20"><div class="alert alert-warning">' . esc_html__('当前模块下没有可显示的幻灯片,请添加幻灯片或移除此模块', 'aksrt') . '</div></div>';
|
||||||
}
|
}
|
||||||
if ($slider === '' && $covers === '') return RendererHelpers::empty_state(__('请配置幻灯片或卡片', 'aksrt'));
|
$html .= '<div class="cover-col mb10">' . $cover_html . '</div>';
|
||||||
$layout = max(0, min(100, (int) ($instance['layout_ratio'] ?? 60)));
|
|
||||||
$slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50));
|
|
||||||
$rows = $pc_row;
|
|
||||||
$columns = max(1, (int) ($instance['pc_col'] ?? 2));
|
|
||||||
$base = $layout < 100 ? (($layout * $slide_scale) / (100 - $layout)) : 0;
|
|
||||||
$cover_ratio = $columns > 0 ? ((($base - (($columns - 1) * 3)) / $columns) * $rows) : 0;
|
|
||||||
$style = '--layout-ratio:' . $layout . '%;--cover-ratio:' . $cover_ratio . '%;--m-cover-ratio:' . $m_cover_ratio . '%;';
|
|
||||||
$animation = '';
|
|
||||||
if (!empty($instance['obs_animation'])) {
|
|
||||||
$animation = ' obs-animate ani-' . sanitize_html_class((string) $instance['obs_animation']) . (!empty($instance['animation_repeat']) ? ' obs-animate-repeat' : '');
|
|
||||||
}
|
|
||||||
$class = 'flex widget-slider-graphic m-' . sanitize_html_class($layout_type);
|
|
||||||
$html = '<div class="' . esc_attr($class) . '" style="' . esc_attr($style) . '" data-row="' . $rows . '" data-col="' . $columns . '">';
|
|
||||||
if ($slider !== '') $html .= '<div class="slider-col mb20' . esc_attr($animation) . '">' . $slider . '</div>';
|
|
||||||
if ($covers !== '') $html .= '<div class="cover-col mb10">' . $covers . '</div>';
|
|
||||||
return $html . '</div>';
|
return $html . '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 参考主题 obs_animation 入场动画类(作用于 slider-col 与每个卡片列)。 */
|
||||||
|
private static function animation_class(array $instance): string {
|
||||||
|
$animation = (string) ($instance['obs_animation'] ?? '');
|
||||||
|
if ($animation === '') return '';
|
||||||
|
$class = ' obs-animate ani-' . sanitize_html_class($animation);
|
||||||
|
if (!empty($instance['animation_repeat'])) $class .= ' obs-animate-repeat';
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -396,25 +396,25 @@ final class WidgetSchemas {
|
|||||||
$asset_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/img/' : '';
|
$asset_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/img/' : '';
|
||||||
$align_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/images/slider-align/' : '';
|
$align_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/images/slider-align/' : '';
|
||||||
$slide_fields = [
|
$slide_fields = [
|
||||||
['id' => 'background', 'type' => 'upload', 'label' => '图片背景', 'default' => ''],
|
['id' => 'background', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'],
|
||||||
['id' => 'background_video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'description' => '图片背景、视频背景至少二选一;PC 端视频优先,移动端只显示图片。'],
|
['id' => 'background_video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片)<div class="c-yellow">如果需要在移动端显示,则必须设置图片背景,移动端只显示图片</div>'],
|
||||||
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
||||||
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
||||||
['id' => 'image_layer', 'type' => 'group', 'label' => '叠加图层', 'max_items' => 12, 'fields' => [
|
['id' => 'image_layer', 'type' => 'group', 'label' => '叠加图层', 'item_label' => '图层', 'button_title' => '添加图层', 'max_items' => 12, 'description' => '添加额外的幻灯片图层,配合图层设置及幻灯片其它设置,可轻松制作出漂亮无比的幻灯片', 'fields' => [
|
||||||
['id' => 'image', 'type' => 'upload', 'label' => '图层图片', 'default' => ''],
|
['id' => 'image', 'type' => 'upload', 'label' => '图层图片', 'default' => '', 'library' => 'image'],
|
||||||
['id' => 'free_size', 'type' => 'switcher', 'label' => '自由尺寸', 'default' => false],
|
['id' => 'free_size', 'type' => 'switcher', 'label' => '自由尺寸', 'default' => false, 'description' => '如果图层的尺寸和背景图的尺寸不一致,可开启此项以自定义图层对齐方向'],
|
||||||
['id' => 'align', 'type' => 'radio', 'label' => '图层对齐', 'default' => 'center', 'options' => ['left' => '靠左显示', 'center' => '居中显示', 'right' => '靠右显示'], 'dependency' => ['image|free_size', '!=|!=', '|']],
|
['id' => 'align', 'type' => 'radio', 'label' => '图层对齐', 'default' => 'center', 'options' => ['left' => '靠左显示', 'center' => '居中显示', 'right' => '靠右显示'], 'dependency' => ['image|free_size', '!=|!=', '|']],
|
||||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '视差滚动', 'default' => 0, 'min' => -200, 'max' => 200, 'step' => 5, 'dependency' => ['image', '!=', '']],
|
['id' => 'parallax', 'type' => 'spinner', 'label' => '视差滚动', 'default' => 0, 'min' => -200, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '提前或延后进入视线,负值为延后,正值为提前,0为关闭[-200~200]', 'dependency' => ['image', '!=', '']],
|
||||||
['id' => 'parallax_scale', 'type' => 'spinner', 'label' => '视差缩放', 'default' => 100, 'min' => 1, 'max' => 200, 'step' => 5, 'dependency' => ['image', '!=', '']],
|
['id' => 'parallax_scale', 'type' => 'spinner', 'label' => '视差缩放', 'default' => 100, 'min' => 1, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '放大或缩小进入视线,原图大小的百分之多少[1~200]', 'dependency' => ['image', '!=', '']],
|
||||||
['id' => 'parallax_opacity', 'type' => 'spinner', 'label' => '视差透明', 'default' => 100, 'min' => 1, 'max' => 100, 'step' => 5, 'dependency' => ['image|parallax', '!=|!=', '|']],
|
['id' => 'parallax_opacity', 'type' => 'spinner', 'label' => '视差透明', 'default' => 100, 'min' => 1, 'max' => 100, 'step' => 5, 'unit' => '%', 'description' => '以百分之多少的透明度进入视线[1~100]<br>视差功能对浏览器性能有一定影响,如果图层较多,不建议全部开启', 'dependency' => ['image|parallax', '!=|!=', '|']],
|
||||||
]],
|
]],
|
||||||
['id' => 'text', 'type' => 'accordion', 'label' => '叠加文案', 'fields' => [
|
['id' => 'text', 'type' => 'accordion', 'label' => '叠加文案', 'fields' => [
|
||||||
['id' => 'title', 'type' => 'textarea', 'label' => '幻灯片文案', 'default' => '', 'allow_html' => true],
|
['id' => 'title', 'type' => 'textarea', 'label' => '幻灯片文案', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1]],
|
||||||
['id' => 'desc', 'type' => 'textarea', 'label' => '幻灯片简介', 'default' => '', 'allow_html' => true, 'dependency' => ['title', '!=', '']],
|
['id' => 'desc', 'type' => 'textarea', 'label' => '幻灯片简介', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '标题、简介均支持HTML代码,请注意代码规范及标签闭合', 'dependency' => ['title', '!=', '']],
|
||||||
['id' => 'text_align', 'type' => 'image_select', 'label' => '显示位置', 'default' => 'left-bottom', 'options' => ['left-bottom' => $align_uri . 'left-bottom.jpg', 'left-conter' => $align_uri . 'left-conter.jpg', 'conter-conter' => $align_uri . 'conter-conter.jpg', 'conter-bottom' => $align_uri . 'conter-bottom.jpg', 'right-conter' => $align_uri . 'right-conter.jpg', 'right-bottom' => $align_uri . 'right-bottom.jpg']],
|
['id' => 'text_align', 'type' => 'image_select', 'label' => '显示位置', 'default' => 'left-bottom', 'description' => '前景图显示位置及文案位置需合理搭配', 'options' => ['left-bottom' => $align_uri . 'left-bottom.jpg', 'left-conter' => $align_uri . 'left-conter.jpg', 'conter-conter' => $align_uri . 'conter-conter.jpg', 'conter-bottom' => $align_uri . 'conter-bottom.jpg', 'right-conter' => $align_uri . 'right-conter.jpg', 'right-bottom' => $align_uri . 'right-bottom.jpg']],
|
||||||
['id' => 'text_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 30, 'min' => 12, 'max' => 50],
|
['id' => 'text_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 30, 'min' => 12, 'max' => 50, 'unit' => 'PX'],
|
||||||
['id' => 'text_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 20, 'min' => 12, 'max' => 50],
|
['id' => 'text_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 20, 'min' => 12, 'max' => 50, 'unit' => 'PX', 'description' => '在此设置标题的字体大小,简介的大小为标题大小的60%,最小12px<br>字体越大,文案周边的间距也越大!字体大小请根据内容合理调整,避免在某些设备显示不全'],
|
||||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '文案视差滚动', 'default' => 40, 'min' => -200, 'max' => 200, 'step' => 10],
|
['id' => 'parallax', 'type' => 'spinner', 'label' => '文案视差滚动', 'default' => 40, 'min' => -200, 'max' => 200, 'step' => 10, 'unit' => '%', 'description' => '视差滚动功能为较背景滚动的时间差,为负数则滚动慢一拍,为正数则滚动快一拍,为0则关闭'],
|
||||||
]],
|
]],
|
||||||
];
|
];
|
||||||
$fields = [
|
$fields = [
|
||||||
@@ -466,10 +466,10 @@ final class WidgetSchemas {
|
|||||||
['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端卡片单行数量', 'default' => 2, 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical'], 'options' => [1 => '1个', 2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个']],
|
['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端卡片单行数量', 'default' => 2, 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical'], 'options' => [1 => '1个', 2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个']],
|
||||||
['id' => 'm_cover_ratio', 'type' => 'spinner', 'label' => '移动端卡片长宽比', 'default' => 45, 'min' => 5, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical']],
|
['id' => 'm_cover_ratio', 'type' => 'spinner', 'label' => '移动端卡片长宽比', 'default' => 45, 'min' => 5, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical']],
|
||||||
['type' => 'subheading', 'content' => '左侧幻灯片'],
|
['type' => 'subheading', 'content' => '左侧幻灯片'],
|
||||||
['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'item_label' => '幻灯片', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields, 'description' => '注意:当添加的幻灯片内容只有一个的时候,则不会以幻灯片的形式展示(不能滑动切换,相当于单个图片或视频)<div class="c-yellow">由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'item_label' => '幻灯片', 'button_title' => '添加幻灯片', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields, 'description' => '注意:当添加的幻灯片内容只有一个的时候,则不会以幻灯片的形式展示(不能滑动切换,相当于单个图片或视频)<div class="c-yellow">由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||||
['id' => 'option', 'type' => 'accordion', 'label' => '幻灯片设置', 'default' => ['button' => true, 'pagination' => true, 'effect' => 'slide', 'spacebetween' => 20, 'speed' => 0, 'interval' => 4, 'autoplay' => true, 'loop' => true], 'fields' => $combo_option],
|
['id' => 'option', 'type' => 'accordion', 'label' => '幻灯片设置', 'default' => ['button' => true, 'pagination' => true, 'effect' => 'slide', 'spacebetween' => 20, 'speed' => 0, 'interval' => 4, 'autoplay' => true, 'loop' => true], 'fields' => $combo_option],
|
||||||
['type' => 'subheading', 'content' => '右侧图文视频卡片'],
|
['type' => 'subheading', 'content' => '右侧图文视频卡片'],
|
||||||
['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'item_label' => '卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => self::cover_fields(), 'description' => '<div class="c-yellow">注意:由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'item_label' => '卡片', 'button_title' => '添加卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => self::cover_fields(), 'description' => '<div class="c-yellow">注意:由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||||
['type' => 'subheading', 'content' => '卡片样式配置'],
|
['type' => 'subheading', 'content' => '卡片样式配置'],
|
||||||
['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 0, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'],
|
['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 0, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'],
|
||||||
['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 16, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'],
|
['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 16, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'],
|
||||||
@@ -589,7 +589,7 @@ final class WidgetSchemas {
|
|||||||
private static function cover_fields(): array {
|
private static function cover_fields(): array {
|
||||||
return [
|
return [
|
||||||
['id' => 'image', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'],
|
['id' => 'image', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'],
|
||||||
['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片),移动端只显示图片'],
|
['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片),<span class="c-yellow">移动端只显示图片</span>'],
|
||||||
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
||||||
['id' => 'title', 'type' => 'textarea', 'label' => '文字', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '支持HTML代码'],
|
['id' => 'title', 'type' => 'textarea', 'label' => '文字', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '支持HTML代码'],
|
||||||
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
||||||
|
|||||||
@@ -97,3 +97,29 @@ test('admin core supports composite dependencies and nested repeater renumbering
|
|||||||
assert.equal(core.dependencyVisible(['image|free_size', '!=|!=', '|'], { image: 'x', free_size: '1' }), true);
|
assert.equal(core.dependencyVisible(['image|free_size', '!=|!=', '|'], { image: 'x', free_size: '1' }), true);
|
||||||
assert.equal(core.replaceRepeaterIndexes('widget[3][0][1]', [4, 2]), 'widget[3][4][2]');
|
assert.equal(core.replaceRepeaterIndexes('widget[3][0][1]', [4, 2]), 'widget[3][4][2]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('slider media combo replicates the reference module front and back', () => {
|
||||||
|
const renderer = read('src/WidgetSystem/Renderers/SliderRenderer.php');
|
||||||
|
const schema = read('src/WidgetSystem/WidgetSchemas.php');
|
||||||
|
const form = read('src/WidgetSystem/FormRenderer.php');
|
||||||
|
|
||||||
|
// 后台表单:参考主题分组按钮文案与上传字段库约束。
|
||||||
|
for (const token of ["'button_title' => '添加幻灯片'", "'button_title' => '添加卡片'", "'button_title' => '添加图层'", "'library' => 'image'", "'library' => 'video'", 'PC端布局配置', '移动端布局方式', '左侧幻灯片', '右侧图文视频卡片', '卡片样式配置']) {
|
||||||
|
assert.match(schema, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||||
|
}
|
||||||
|
assert.match(form, /\$field\['button_title'\]/);
|
||||||
|
assert.match(form, /\$library !== 'video'/);
|
||||||
|
|
||||||
|
// 前端:幻灯片 span/a 包裹、懒加载契约、视频背景与图层视差。
|
||||||
|
for (const token of ["'<span>' . $body . '</span>'", 'lazyload swiper-lazy', 'slide-background-video', "aksrt_is_lazy('lazy_sider')", 'data-swiper-parallax-opacity', 'data-swiper-parallax-scale']) {
|
||||||
|
assert.match(renderer, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||||
|
}
|
||||||
|
assert.doesNotMatch(renderer, /playsinline/);
|
||||||
|
|
||||||
|
// 组合模块:is_show 门控、管理端缺省提示、卡片列动画与比例变量。
|
||||||
|
for (const token of ["aksrt_is_lazy('lazy_cover')", 'alert alert-warning', '当前模块下没有可显示的幻灯片', 'obs-animate ani-', "widget-graphic-cover ' . ($is_row ? 'mb10' : 'mb20')", 'row gutters-5', 'col-xs-', 'graphic-text this-font', 'm-cover-ratio', 'cover-ratio', 'layout-ratio', 'data-row=', 'data-col=', "m-' . sanitize_html_class($m_layout_type)"]) {
|
||||||
|
assert.match(renderer, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||||
|
}
|
||||||
|
// 参考主题 is_show:两侧均为空时不输出占位。
|
||||||
|
assert.doesNotMatch(renderer, /请配置幻灯片或卡片|empty_state/);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user