mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 01:08:59 +00:00
FIX - polish House header and sheets
Respect the shared phone safe area, keep the House subtitle visible, and align the keys list with the sheet content width. Make the shared sheet grabber optionally clickable while preserving swipe-to-close behavior and add focused contract coverage.
This commit is contained in:
@@ -223,9 +223,14 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
cursor: ns-resize;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
|
||||
@@ -30,6 +30,19 @@ describe('SkySheet', () => {
|
||||
expect(ordinary).not.toContain('sky-sheet__grabber')
|
||||
})
|
||||
|
||||
it('can expose the drag handle as an accessible close button', async () => {
|
||||
const clickable = await renderSheet({
|
||||
grabberClickable: true,
|
||||
grabberLabel: 'Close property details',
|
||||
opened: true,
|
||||
swipeToClose: true,
|
||||
})
|
||||
|
||||
expect(clickable).toContain('<button')
|
||||
expect(clickable).toContain('class="sky-sheet__grabber"')
|
||||
expect(clickable).toContain('aria-label="Close property details"')
|
||||
})
|
||||
|
||||
it('owns pointer capture, close thresholds, and settling motion', () => {
|
||||
const source = readFileSync(
|
||||
new URL('./SkySheet.vue', import.meta.url),
|
||||
@@ -41,6 +54,7 @@ describe('SkySheet', () => {
|
||||
)
|
||||
|
||||
expect(source).toContain('swipeclose: [event: PointerEvent]')
|
||||
expect(source).toContain('grabberclick: [event: MouseEvent]')
|
||||
expect(source).toContain('setPointerCapture(event.pointerId)')
|
||||
expect(source).toContain('dragOffset.value >= closeThreshold')
|
||||
expect(source).toContain("emit('swipeclose', event)")
|
||||
|
||||
@@ -12,6 +12,8 @@ const props = withDefaults(
|
||||
ariaModal?: boolean | 'false' | 'true'
|
||||
backdrop?: boolean
|
||||
component?: string
|
||||
grabberClickable?: boolean
|
||||
grabberLabel?: string
|
||||
opened: boolean
|
||||
role?: 'alertdialog' | 'dialog' | 'none' | 'presentation'
|
||||
swipeToClose?: boolean
|
||||
@@ -21,12 +23,15 @@ const props = withDefaults(
|
||||
ariaModal: true,
|
||||
backdrop: true,
|
||||
component: 'div',
|
||||
grabberClickable: false,
|
||||
grabberLabel: '',
|
||||
tabindex: -1,
|
||||
},
|
||||
)
|
||||
const emit = defineEmits<{
|
||||
backdropclick: [event: MouseEvent]
|
||||
escape: [event: KeyboardEvent]
|
||||
grabberclick: [event: MouseEvent]
|
||||
swipeclose: [event: PointerEvent]
|
||||
}>()
|
||||
|
||||
@@ -200,16 +205,20 @@ useOverlayFocusTrap({
|
||||
:aria-describedby="effectiveRole ? ariaDescribedby : undefined"
|
||||
:tabindex="tabindex"
|
||||
>
|
||||
<div
|
||||
<component
|
||||
:is="grabberClickable ? 'button' : 'div'"
|
||||
v-if="swipeToClose"
|
||||
class="sky-sheet__grabber"
|
||||
aria-hidden="true"
|
||||
:type="grabberClickable ? 'button' : undefined"
|
||||
:aria-hidden="grabberClickable ? undefined : true"
|
||||
:aria-label="grabberClickable ? grabberLabel : undefined"
|
||||
@click="grabberClickable && emit('grabberclick', $event)"
|
||||
@lostpointercapture="finishDrag($event, true)"
|
||||
@pointercancel="finishDrag($event, true)"
|
||||
@pointerdown="startDrag"
|
||||
@pointermove="moveDrag"
|
||||
@pointerup="finishDrag($event)"
|
||||
></div>
|
||||
></component>
|
||||
<slot />
|
||||
</component>
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,13 @@ import { describe, expect, it } from 'vitest'
|
||||
const source = readFileSync(new URL('./HouseApp.vue', import.meta.url), 'utf8')
|
||||
|
||||
describe('House app sheets', () => {
|
||||
it('closes both sheets through their shared drag gesture', () => {
|
||||
it('closes both sheets through their shared drag gesture or grabber button', () => {
|
||||
expect(source.match(/swipe-to-close/g)).toHaveLength(2)
|
||||
expect(source.match(/grabber-clickable/g)).toHaveLength(2)
|
||||
expect(source).toContain('@swipeclose="selectedPropertyId = null"')
|
||||
expect(source).toContain('@swipeclose="candidatesOpened = false"')
|
||||
expect(source).toContain('@grabberclick="selectedPropertyId = null"')
|
||||
expect(source).toContain('@grabberclick="candidatesOpened = false"')
|
||||
})
|
||||
|
||||
it('sizes the panels instead of clipping the overlay roots', () => {
|
||||
@@ -23,4 +26,21 @@ describe('House app sheets', () => {
|
||||
)
|
||||
expect(source).not.toContain('height: 620px;')
|
||||
})
|
||||
|
||||
it('keeps the header clear of the status bar and shows its full subtitle', () => {
|
||||
expect(source).not.toContain('--sky-safe-area-top: 46px')
|
||||
expect(source).toMatch(
|
||||
/\.house-navbar :deep\(\.sky-navbar__heading\)\s*\{[^}]*grid-column:\s*1 \/ -1;/s,
|
||||
)
|
||||
expect(source).toMatch(
|
||||
/\.house-navbar :deep\(\.sky-navbar__subtitle\)\s*\{[^}]*text-overflow:\s*clip;[^}]*white-space:\s*normal;/s,
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the full popup content width for the keys list', () => {
|
||||
expect(source).toContain('class="house-key-list"')
|
||||
expect(source).toMatch(
|
||||
/\.house-key-list\s*\{[^}]*--sky-list-outer-left:\s*0px;[^}]*--sky-list-outer-right:\s*0px;/s,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -363,9 +363,12 @@ onBeforeUnmount(() => {
|
||||
<sky-sheet
|
||||
:opened="Boolean(selectedProperty)"
|
||||
class="house-detail-sheet"
|
||||
grabber-clickable
|
||||
:grabber-label="phone.t('Common.close')"
|
||||
swipe-to-close
|
||||
@backdropclick="selectedPropertyId = null"
|
||||
@escape="selectedPropertyId = null"
|
||||
@grabberclick="selectedPropertyId = null"
|
||||
@swipeclose="selectedPropertyId = null"
|
||||
>
|
||||
<section v-if="selectedProperty" class="house-detail">
|
||||
@@ -505,7 +508,12 @@ onBeforeUnmount(() => {
|
||||
<Plus :size="15" />{{ phone.t('Apps.house.addKey') }}
|
||||
</sky-button>
|
||||
</header>
|
||||
<sky-list v-if="selectedProperty.keys?.length" inset strong>
|
||||
<sky-list
|
||||
v-if="selectedProperty.keys?.length"
|
||||
inset
|
||||
strong
|
||||
class="house-key-list"
|
||||
>
|
||||
<sky-list-item
|
||||
v-for="key in selectedProperty.keys"
|
||||
:key="key.identifier"
|
||||
@@ -542,9 +550,12 @@ onBeforeUnmount(() => {
|
||||
<sky-sheet
|
||||
:opened="candidatesOpened"
|
||||
class="house-candidates-sheet"
|
||||
grabber-clickable
|
||||
:grabber-label="phone.t('Common.close')"
|
||||
swipe-to-close
|
||||
@backdropclick="candidatesOpened = false"
|
||||
@escape="candidatesOpened = false"
|
||||
@grabberclick="candidatesOpened = false"
|
||||
@swipeclose="candidatesOpened = false"
|
||||
>
|
||||
<section class="house-candidates">
|
||||
@@ -635,7 +646,6 @@ onBeforeUnmount(() => {
|
||||
linear-gradient(165deg, #26201d 0, #111216 48%, #171d25 100%);
|
||||
}
|
||||
.house-navbar {
|
||||
--sky-safe-area-top: 46px;
|
||||
--sky-navbar-glass: transparent;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
@@ -648,19 +658,29 @@ onBeforeUnmount(() => {
|
||||
opacity: 0;
|
||||
}
|
||||
.house-navbar :deep(.sky-navbar__heading) {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
grid-column: 1 / -1;
|
||||
gap: 1px;
|
||||
padding: 0 var(--sky-page-gutter);
|
||||
}
|
||||
.house-navbar :deep(.sky-navbar__title) {
|
||||
line-height: 24px;
|
||||
}
|
||||
.house-navbar :deep(.sky-navbar__subtitle) {
|
||||
max-width: 100%;
|
||||
margin-top: 0;
|
||||
overflow: visible;
|
||||
line-height: 15px;
|
||||
text-overflow: clip;
|
||||
white-space: normal;
|
||||
}
|
||||
.house-scroll {
|
||||
position: absolute;
|
||||
inset: 104px 0 25px;
|
||||
inset: calc(
|
||||
var(--sky-safe-area-top) + var(--sky-navbar-height) + var(--sky-space-3)
|
||||
)
|
||||
0 25px;
|
||||
padding: 9px 13px 34px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
@@ -685,7 +705,10 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.house-state {
|
||||
position: absolute;
|
||||
inset: 104px 0 25px;
|
||||
inset: calc(
|
||||
var(--sky-safe-area-top) + var(--sky-navbar-height) + var(--sky-space-3)
|
||||
)
|
||||
0 25px;
|
||||
padding: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1228,6 +1251,13 @@ onBeforeUnmount(() => {
|
||||
.house-keys {
|
||||
padding: 0 16px;
|
||||
}
|
||||
.house-key-list {
|
||||
--sky-list-outer-left: 0px;
|
||||
--sky-list-outer-right: 0px;
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.house-keys header {
|
||||
margin: 20px 4px 8px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user