fix(manage-sheet): clearer copy + visual hierarchy in row controls
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
Matt called out the row was confusing: lock pill said "Rotate" (sounds
like a verb), and the toggle's purpose wasn't obvious.
- Drop the "Rotate" word entirely. Lock pill is icon-only when
unlocked, shows "Locked" + closed padlock when locked.
- Hide the lock pill entirely when the photo isn't approved on the
frame (instead of rendering a disabled one) — keeps the row clean
and reinforces that locking requires approval first.
- Add a tiny "Show" / "Hidden" label above the toggle so the meaning
reads before the user taps. Toggle is now the visual primary on
the row.
- Re-label aria-text from "Add/Remove" to "Show/Hide" to match the
visible copy.
Test "disables the lock pill when not approved" → "hides the lock pill
when not approved". 358/358 still passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,11 +25,16 @@
|
||||
<span class="manage__device-meta">{{ device.orientation }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Lock control. Hidden entirely when the photo isn't approved on
|
||||
this frame — no need to show a disabled lock when it can't
|
||||
apply. When locked, shows "Locked" + closed padlock; otherwise
|
||||
icon-only so the toggle stays the visual primary. -->
|
||||
<button
|
||||
v-if="isApproved(device.id)"
|
||||
type="button"
|
||||
class="manage__lock"
|
||||
:class="{ 'manage__lock--on': device.lockedImageId === image?.id }"
|
||||
:disabled="!isApproved(device.id) || pendingLock === device.id"
|
||||
:disabled="pendingLock === device.id"
|
||||
:aria-label="device.lockedImageId === image?.id
|
||||
? `Unlock from ${device.name}`
|
||||
: `Lock to ${device.name}`"
|
||||
@@ -40,19 +45,25 @@
|
||||
<path v-if="device.lockedImageId === image?.id" d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
<path v-else d="M7 11V7a5 5 0 0 1 9.9-1"/>
|
||||
</svg>
|
||||
<span>{{ device.lockedImageId === image?.id ? 'Locked' : 'Rotate' }}</span>
|
||||
<span v-if="device.lockedImageId === image?.id">Locked</span>
|
||||
</button>
|
||||
|
||||
<!-- Show / hide this photo on this frame. Primary control on the row. -->
|
||||
<label class="manage__toggle-wrap">
|
||||
<span class="manage__toggle-label">
|
||||
{{ isApproved(device.id) ? 'Show' : 'Hidden' }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="manage__toggle"
|
||||
:class="{ 'manage__toggle--on': isApproved(device.id) }"
|
||||
:disabled="pendingApproval === device.id"
|
||||
:aria-label="isApproved(device.id)
|
||||
? `Remove this photo from ${device.name}`
|
||||
: `Add this photo to ${device.name}`"
|
||||
? `Hide this photo from ${device.name}`
|
||||
: `Show this photo on ${device.name}`"
|
||||
@click="onApprovalClick(device)"
|
||||
></button>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -195,6 +206,24 @@ function onLockClick(device: Device) {
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide toggle group — label above the slider so the meaning is
|
||||
// obvious before tapping.
|
||||
&__toggle-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__toggle-label {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 600;
|
||||
color: var(--color-text-muted);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// iOS-style approval toggle
|
||||
&__toggle {
|
||||
width: 48px;
|
||||
|
||||
@@ -94,13 +94,14 @@ describe('ManageImageSheet', () => {
|
||||
expect(w.emitted('approval')![0][0]).toEqual({ imageId: 7, deviceId: 4, approved: false })
|
||||
})
|
||||
|
||||
it('disables the lock pill when the image is not approved on the device', () => {
|
||||
it('hides the lock pill when the image is not approved on the device', () => {
|
||||
// The pill can't apply when the photo isn't approved on the frame —
|
||||
// hiding (rather than disabling) keeps the row visually clean.
|
||||
const w = mountSheet({
|
||||
image: makeImage({ approvedDeviceIds: [] }),
|
||||
devices: [makeDevice({ id: 1 })],
|
||||
})
|
||||
const lock = w.find('.manage__lock')
|
||||
expect(lock.attributes('disabled')).toBeDefined()
|
||||
expect(w.find('.manage__lock').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows the lock pill in --on state when the device is locked to this image', () => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="pictureFrame" />
|
||||
<script type="module" crossorigin src="/build/assets/index-Ds9OAB3e.js"></script>
|
||||
<script type="module" crossorigin src="/build/assets/index-DuLafD-s.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/build/assets/_plugin-vue_export-helper-BNDVmFr7.js">
|
||||
<link rel="stylesheet" crossorigin href="/build/assets/index-BlLBHR1q.css">
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user