diff --git a/frontend/src/components/LifeRatingControl.vue b/frontend/src/components/LifeRatingControl.vue new file mode 100644 index 0000000..2cd5e24 --- /dev/null +++ b/frontend/src/components/LifeRatingControl.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index bf6de30..0e1598c 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -2032,59 +2032,6 @@ button:disabled, white-space: pre-wrap; } -.life-rating { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px 12px; -} - -.life-rating__stars { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.life-rating__star { - width: 44px; - min-width: 44px; - min-height: 44px; - display: inline-grid; - place-items: center; - border: 1px solid var(--line); - border-radius: var(--radius-control); - background: var(--surface-soft); - color: color-mix(in srgb, var(--warning) 80%, var(--ink-soft)); - cursor: pointer; - transition: - background 0.14s ease, - border-color 0.14s ease, - color 0.14s ease; -} - -.life-rating__star:hover, -.life-rating__star.is-active { - border-color: color-mix(in srgb, var(--warning) 72%, var(--line)); - background: color-mix(in srgb, var(--warning) 16%, var(--surface-soft)); - color: color-mix(in srgb, var(--warning) 84%, var(--ink)); -} - -.life-rating__star:disabled { - cursor: not-allowed; - opacity: 0.56; -} - -.life-rating__star .ui-icon { - width: 22px; - height: 22px; -} - -.life-rating__summary { - color: var(--muted); - font-size: 14px; - font-weight: 850; -} - .life-post__engagement { display: flex; flex-wrap: wrap; @@ -2103,11 +2050,105 @@ button:disabled, gap: 8px; } +.life-post__engagement-actions { + flex: 1 1 520px; +} + .life-post__metrics { justify-content: flex-end; min-width: 0; } +.life-rating-control { + min-height: 44px; + height: 44px; + display: inline-flex; + align-items: center; + flex: 0 0 auto; + gap: 6px; + min-width: 0; + max-width: 100%; + padding: 0 8px 0 0; + border: 0; + border-radius: var(--radius-control); + background: var(--surface-soft); + box-shadow: inset 0 0 0 1px var(--line); + color: var(--ink-soft); + font-weight: 900; +} + +.life-rating-control__stars { + min-height: 44px; + height: 44px; + display: inline-flex; + align-items: center; + flex: 0 0 auto; + gap: 0; + overflow: hidden; + border-radius: calc(var(--radius-control) - 1px) 0 0 calc(var(--radius-control) - 1px); +} + +.life-rating-control__star { + position: relative; + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + display: inline-grid; + place-items: center; + flex: 0 0 44px; + border: 1px solid transparent; + border-radius: 0; + background: transparent; + color: color-mix(in srgb, var(--warning) 78%, var(--ink-soft)); + cursor: pointer; + touch-action: manipulation; + transition: + background 0.14s ease, + border-color 0.14s ease, + color 0.14s ease, + transform 0.14s ease; +} + +.life-rating-control__star:hover, +.life-rating-control__star:focus-visible, +.life-rating-control__star.is-active { + border-color: transparent; + background: color-mix(in srgb, var(--warning) 16%, var(--surface-soft)); + color: color-mix(in srgb, var(--warning) 86%, var(--ink)); +} + +.life-rating-control__star:hover { + transform: none; +} + +.life-rating-control__star:disabled { + cursor: not-allowed; + opacity: 0.55; + transform: none; +} + +.life-rating-control__star .ui-icon { + width: 19px; + height: 19px; +} + +.life-rating-control__summary { + min-width: 24px; + height: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 2px; + color: var(--ink-soft); + font-size: 13px; + font-weight: 900; + font-variant-numeric: tabular-nums; + line-height: 1.25; + overflow-wrap: anywhere; + white-space: nowrap; +} + .life-icon-button, .life-metric-button { position: relative; @@ -2180,13 +2221,28 @@ button:disabled, position: relative; } +.life-post__review-actions { + min-height: 44px; + display: inline-flex; + align-items: center; + gap: 8px; + min-width: 0; +} + +.life-review-button { + height: 44px; + min-height: 44px; +} + .life-reaction-control { + height: 44px; display: inline-flex; align-items: stretch; overflow: visible; - border: 1px solid var(--line); + border: 0; border-radius: var(--radius-control); background: var(--surface-soft); + box-shadow: inset 0 0 0 1px var(--line); } .life-reaction-control .life-icon-button { @@ -5939,6 +5995,11 @@ button:disabled, justify-content: flex-start; } + .life-rating-control { + flex-wrap: nowrap; + justify-content: flex-start; + } + .life-toolbar, .life-toolbar__search, .life-toolbar__filters { diff --git a/frontend/src/views/LifeView.vue b/frontend/src/views/LifeView.vue index f95f8ea..c89b263 100644 --- a/frontend/src/views/LifeView.vue +++ b/frontend/src/views/LifeView.vue @@ -3,6 +3,7 @@ import { Icon } from '@iconify/vue'; import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import FilterPanel from '../components/FilterPanel.vue'; +import LifeRatingControl from '../components/LifeRatingControl.vue'; import Modal from '../components/Modal.vue'; import PageHeader from '../components/PageHeader.vue'; import Skeleton from '../components/Skeleton.vue'; @@ -25,8 +26,6 @@ import { iconReply, iconSave, iconSearch, - iconStar, - iconStarOutline, iconVersion, iconWarning } from '../icons'; @@ -727,21 +726,6 @@ function canUseRatings(post: LifePost) { return canRate.value && ratingBusyPostId.value === null && post.moderationStatus === 'approved' && post.category?.isRateable === true; } -function ratingButtonLabel(post: LifePost, rating: number) { - return post.myRating === rating ? t('pages.life.removeRating') : t('pages.life.setRating', { count: rating }); -} - -function ratingAverageLabel(post: LifePost) { - if (post.ratingAverage === null || post.ratingCount === 0) { - return t('pages.life.noRatings'); - } - - return t('pages.life.ratingAverage', { - average: new Intl.NumberFormat(locale.value, { maximumFractionDigits: 2 }).format(post.ratingAverage), - count: post.ratingCount - }); -} - function closeReactionPicker() { reactionPickerPostId.value = null; } @@ -1256,21 +1240,6 @@ onUnmounted(() => { -
{{ moderationErrors[post.id] }}
-{{ post.body }}