Merge pull request 'fix(widgets): stabilize user card layout' (#14) from worktree/fix-tool-user into main

Reviewed-on: #14
This commit is contained in:
2026-08-14 13:16:18 +00:00
4 changed files with 176 additions and 48 deletions
+128 -18
View File
@@ -22,61 +22,170 @@
}
.aksrt-user-card {
border-radius: 12px;
overflow: hidden;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 100%;
min-width: 0;
border-radius: 8px;
overflow: visible;
border: 1px solid var(--color-line, #e5e7eb);
background: var(--surface-1, #fff);
text-align: center;
margin-bottom: 0;
}
.aksrt-user-card__cover {
height: 90px;
background: linear-gradient(135deg, #93c5fd, #c4b5fd) center/cover no-repeat;
position: relative;
box-sizing: border-box;
width: 100%;
height: 104px;
min-height: 104px;
max-height: 104px;
margin: 0;
padding: 0 !important;
overflow: hidden;
border-radius: 7px 7px 0 0;
background-color: #93c5fd;
background-image: linear-gradient(135deg, #93c5fd, #c4b5fd);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.aksrt-user-card__cover.is-empty {
background: linear-gradient(135deg, #e2e8f0, #f8fafc);
}
.aksrt-user-card__body { padding: 0 14px 16px; position: relative; }
.aksrt-user-card__body {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
min-width: 0;
padding: 0 16px 18px;
}
.aksrt-user-card__checkin {
position: absolute;
right: 10px;
top: 8px;
z-index: 2;
max-width: 48%;
top: 66%;
right: 0;
z-index: 3;
max-width: calc(100% - 56px);
transform: translateY(-50%);
}
.aksrt-user-card__checkin .but {
padding: 4px 8px !important;
font-size: 12px !important;
line-height: 1.2;
.aksrt-user-card__checkin--without-cover {
position: relative;
top: auto;
right: auto;
display: flex;
justify-content: flex-end;
max-width: none;
margin: 10px 0 0;
transform: none;
}
.aksrt-user-card .aksrt-user-card__checkin .aksrt-header-checkin {
width: auto !important;
min-height: 40px;
padding: 8px 12px !important;
border-radius: 6px 0 0 6px !important;
font-size: 13px !important;
line-height: 1.25;
}
.aksrt-user-card__checkin .aksrt-header-checkin-details { display: none; }
.aksrt-user-card__avatar {
.aksrt-user-card__avatar-wrap {
position: relative;
display: inline-block;
margin-top: -36px;
width: 72px;
height: 72px;
flex: 0 0 auto;
width: 76px;
height: 76px;
margin: -38px auto 0;
}
.aksrt-user-card.no-cover .aksrt-user-card__avatar-wrap {
margin-top: 16px;
}
.aksrt-user-card__avatar {
display: block;
box-sizing: border-box;
width: 76px;
height: 76px;
border-radius: 50%;
overflow: hidden;
border: 3px solid var(--surface-1, #fff);
background: var(--surface-2, #eee);
}
.aksrt-user-card__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.aksrt-user-card__avatar > a {
display: block;
width: 100%;
height: 100%;
}
.aksrt-user-card__avatar > img,
.aksrt-user-card__avatar > a > img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.aksrt-user-card__vip {
position: absolute;
right: -5px;
bottom: -3px;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
padding: 3px;
border: 2px solid var(--surface-1, #fff);
border-radius: 50%;
background: var(--surface-1, #fff);
box-shadow: 0 2px 6px rgba(0, 0, 0, .18);
line-height: 1;
}
.aksrt-user-card__vip .aksrt-vip-badge {
width: 100%;
height: 100%;
}
.aksrt-user-card__vip .aksrt-vip-badge__icon {
width: 100%;
height: 100%;
max-width: 100%;
object-fit: contain;
}
.aksrt-user-card__name {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-width: 0;
margin: 10px 0 0;
font-size: 16px;
font-weight: 700;
line-height: 1.35;
}
.aksrt-user-card__name-row {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 0;
max-width: 100%;
gap: 4px;
}
.aksrt-user-card__name .display-name {
min-width: 0;
max-width: 100%;
}
.aksrt-user-card__name a { color: inherit; text-decoration: none; }
.aksrt-user-card__bio,
.aksrt-user-card__guest-title {
width: 100%;
margin: 8px 0 0;
font-size: 12px;
color: var(--muted-2-color, #888);
line-height: 1.5;
overflow-wrap: anywhere;
}
.aksrt-user-card__guest-title { font-size: 14px; font-weight: 600; color: inherit; }
.aksrt-user-card__stats {
display: flex;
width: 100%;
justify-content: center;
gap: 18px;
margin-top: 12px;
@@ -92,6 +201,7 @@
}
.aksrt-user-card__actions {
display: flex;
width: 100%;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
+4 -1
View File
@@ -119,9 +119,12 @@
btn.disabled = true;
btn.setAttribute('disabled', 'disabled');
// Keep existing structure so CSS layout (.but.block flex) is not lost.
var signedLabel = btn.classList.contains('aksrt-header-checkin--compact')
? '已签到'
: '今日已签到';
btn.innerHTML =
'<i class="fas fa-calendar-check" aria-hidden="true"></i>' +
'<span>今日已签到</span>';
'<span>' + signedLabel + '</span>';
})
.catch(function () {
toast('网络错误', 'danger');
+10 -6
View File
@@ -9,9 +9,12 @@ defined('ABSPATH') || exit;
if (!function_exists('aksrt_header_user_checkin_btn')) :
/**
* Check-in CTA for header dropdown (uses REST /aksrt/v1/signin).
* Check-in CTA for header dropdown and compact card placements (uses REST /aksrt/v1/signin).
*
* @param int $user_id Current user ID.
* @param bool $compact Render a short card-friendly label.
*/
function aksrt_header_user_checkin_btn(int $user_id = 0): string {
function aksrt_header_user_checkin_btn(int $user_id = 0, bool $compact = false): string {
$user_id = $user_id > 0 ? $user_id : get_current_user_id();
if ($user_id <= 0 || !class_exists('\AKSRT\Credits\Signin')) {
return '';
@@ -39,16 +42,17 @@ if (!function_exists('aksrt_header_user_checkin_btn')) :
$signed = false;
}
$compact_class = $compact ? ' aksrt-header-checkin--compact' : '';
if ($signed) {
return '<button type="button" class="but block c-yellow aksrt-header-checkin is-done" disabled>'
return '<button type="button" class="but block c-yellow aksrt-header-checkin' . $compact_class . ' is-done" disabled>'
. '<i class="fas fa-calendar-check" aria-hidden="true"></i>'
. '<span>' . esc_html__('今日已签到', 'aksrt') . '</span>'
. '<span>' . esc_html($compact ? __('已签到', 'aksrt') : __('今日已签到', 'aksrt')) . '</span>'
. '</button>';
}
return '<button type="button" class="but block c-yellow aksrt-header-checkin" data-aksrt-checkin="1">'
return '<button type="button" class="but block c-yellow aksrt-header-checkin' . $compact_class . '" data-aksrt-checkin="1">'
. '<i class="fas fa-calendar-check" aria-hidden="true"></i>'
. '<span>' . esc_html__('签到领取今日奖励', 'aksrt') . '</span>'
. '<span>' . esc_html($compact ? __('签到', 'aksrt') : __('签到领取今日奖励', 'aksrt')) . '</span>'
. '</button>';
}
endif;
+34 -23
View File
@@ -7,13 +7,9 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetUserCard extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
'aksrt_user_card',
@@ -122,13 +118,28 @@ class WidgetUserCard extends \WP_Widget {
}
$name_html = function_exists('aksrt_get_user_name')
? aksrt_get_user_name(['id' => $user_id, 'follow' => !$is_self, 'class' => 'flex ac jc'])
? aksrt_get_user_name([
'id' => $user_id,
'vip' => false,
'follow' => !$is_self,
'class' => 'aksrt-user-card__name-row',
])
: '<a href="' . esc_url($profile_url) . '">' . esc_html($user->display_name) . '</a>';
$vip_badge = function_exists('aksrt_render_vip_badge')
? (string) aksrt_render_vip_badge($user_id, 'sm')
: '';
$checkin_btn = '';
if ($show_checkin && $is_self && function_exists('aksrt_header_user_checkin_btn')) {
$checkin_btn = aksrt_header_user_checkin_btn($user_id, true);
}
$checkin_html = $checkin_btn !== ''
? '<div class="aksrt-user-card__checkin">' . $checkin_btn . '</div>'
: '';
echo '<div class="aksrt-user-card user-card">';
echo '<div class="aksrt-user-card user-card' . ($show_cover ? ' has-cover' : ' no-cover') . '">';
if ($show_cover) {
if ($cover !== '') {
echo '<div class="aksrt-user-card__cover user-cover graphic" style="background-image:url(' . esc_url($cover) . ')"></div>';
echo '<div class="aksrt-user-card__cover" style="background-image:url(' . esc_url($cover) . ')">' . $checkin_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
// Prefer site default cover image when available.
$default_cover = '';
@@ -139,22 +150,23 @@ class WidgetUserCard extends \WP_Widget {
$default_cover = (string) get_theme_mod('aksrt_user_cover', '');
}
if ($default_cover !== '') {
echo '<div class="aksrt-user-card__cover user-cover graphic" style="background-image:url(' . esc_url($default_cover) . ')"></div>';
echo '<div class="aksrt-user-card__cover" style="background-image:url(' . esc_url($default_cover) . ')">' . $checkin_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="aksrt-user-card__cover user-cover graphic is-empty"></div>';
echo '<div class="aksrt-user-card__cover is-empty">' . $checkin_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
} elseif ($checkin_btn !== '') {
echo '<div class="aksrt-user-card__checkin aksrt-user-card__checkin--without-cover">' . $checkin_btn . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '<div class="aksrt-user-card__body card-content">';
if ($show_checkin && $is_self && function_exists('aksrt_header_user_checkin_btn')) {
$btn = aksrt_header_user_checkin_btn($user_id);
if ($btn !== '') {
echo '<div class="aksrt-user-card__checkin">' . $btn . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
echo '<div class="aksrt-user-card__avatar-wrap">';
echo '<div class="aksrt-user-card__avatar user-avatar">';
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
echo '<a href="' . esc_url($profile_url) . '"><img class="aksrt-lazy lazyload" data-src="' . esc_url($avatar_url) . '" src="' . esc_url($ph) . '" alt="' . esc_attr($user->display_name) . '" loading="lazy" width="72" height="72" /></a>';
echo '<a href="' . esc_url($profile_url) . '"><img class="aksrt-lazy lazyload" data-src="' . esc_url($avatar_url) . '" src="' . esc_url($ph) . '" alt="' . esc_attr($user->display_name) . '" loading="lazy" width="76" height="76" /></a>';
echo '</div>';
if ($vip_badge !== '') {
echo '<span class="aksrt-user-card__vip">' . $vip_badge . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '</div>';
echo '<h4 class="aksrt-user-card__name user-name">' . $name_html . '</h4>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($bio !== '') {
@@ -212,20 +224,20 @@ class WidgetUserCard extends \WP_Widget {
}
}
echo '<div class="aksrt-user-card user-card is-guest">';
echo '<div class="aksrt-user-card user-card is-guest' . ($show_cover ? ' has-cover' : ' no-cover') . '">';
if ($show_cover) {
$default_cover = '';
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_user_cover_default')) {
$default_cover = (string) \AKSRT\Settings\Settings::get_user_cover_default();
}
if ($default_cover !== '') {
echo '<div class="aksrt-user-card__cover user-cover graphic" style="background-image:url(' . esc_url($default_cover) . ')"></div>';
echo '<div class="aksrt-user-card__cover" style="background-image:url(' . esc_url($default_cover) . ')"></div>';
} else {
echo '<div class="aksrt-user-card__cover user-cover graphic is-empty"></div>';
echo '<div class="aksrt-user-card__cover is-empty"></div>';
}
}
echo '<div class="aksrt-user-card__body card-content">';
echo '<div class="aksrt-user-card__avatar user-avatar"><img src="' . esc_url($default_avatar) . '" alt="" width="72" height="72" loading="lazy" /></div>';
echo '<div class="aksrt-user-card__avatar-wrap"><div class="aksrt-user-card__avatar user-avatar"><img src="' . esc_url($default_avatar) . '" alt="" width="76" height="76" loading="lazy" /></div></div>';
echo '<div class="aksrt-user-card__guest-title">' . esc_html($loged_title) . '</div>';
echo '<div class="aksrt-user-card__actions">';
echo '<a class="aksrt-user-card__btn aksrt-user-card__btn--primary signin-loader" href="' . esc_url($login) . '">' . esc_html__('登录', 'aksrt') . '</a>';
@@ -320,7 +332,6 @@ class WidgetUserCard extends \WP_Widget {
</select>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -333,7 +344,7 @@ class WidgetUserCard extends \WP_Widget {
if (!in_array($orderby, ['date', 'views', 'comment_count', 'modified', 'rand'], true)) {
$orderby = 'date';
}
return $this->update_common_fields($new_instance, $old_instance, [
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'loged_title' => sanitize_text_field((string) ($new_instance['loged_title'] ?? '')),
@@ -351,7 +362,7 @@ class WidgetUserCard extends \WP_Widget {
'limit' => max(0, min(12, (int) ($new_instance['limit'] ?? 6))),
'post_style' => $post_style,
'orderby' => $orderby,
]);
];
}
/** Zibll CFS_Module::zib_palette() gradient/solid keys (c/b/jb families). */