ENH - refine phone liquid glass controls

This commit is contained in:
smx.pusha
2026-08-20 11:50:41 +02:00
parent 5efd806e43
commit b9f8930f00
5 changed files with 362 additions and 208 deletions
+12
View File
@@ -111,6 +111,18 @@
color: var(--sky-text, #111827);
}
.sky-glass.sky-button--glass {
border-color: var(--sky-hairline, rgba(0, 0, 0, 0.2));
background: var(--sky-glass, rgba(255, 255, 255, 0.75));
color: var(--sky-text, #000000);
box-shadow: var(--sky-shadow-glass);
}
.sky-glass.sky-button--glass:active:not(:disabled) {
background: var(--sky-glass, rgba(255, 255, 255, 0.75));
filter: brightness(0.94);
}
.sky-button--danger {
background: var(--sky-danger, #dc2626);
}
@@ -28,6 +28,27 @@ describe('SkyButton', () => {
expect(html).toContain('Continue')
})
it('renders interactive liquid glass buttons through the shared glass surface', async () => {
const html = await renderToString(
createSSRApp({
render: () =>
h(SkyButton, { glass: true, rounded: true }, () => 'Edit'),
}),
)
expect(html).toContain('sky-button--glass')
expect(html).toContain('sky-glass')
expect(html).toContain('sky-glass--interactive')
const controls = readFileSync(
fileURLToPath(new URL('../controls.css', import.meta.url)),
'utf8',
)
expect(controls).toMatch(
/\.sky-glass\.sky-button--glass\s*\{[^}]*background:\s*var\(--sky-glass[^}]*box-shadow:\s*var\(--sky-shadow-glass\)/s,
)
})
it('keeps focus and pressed feedback on the contextual accent', () => {
const uiDirectory = fileURLToPath(new URL('..', import.meta.url))
const controls = readFileSync(`${uiDirectory}/controls.css`, 'utf8')
+36 -15
View File
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { computed } from 'vue'
import SkyGlass from './SkyGlass.vue'
defineOptions({ inheritAttrs: false })
const props = withDefaults(
@@ -9,6 +11,7 @@ const props = withDefaults(
clear?: boolean
component?: 'a' | 'button'
disabled?: boolean
glass?: boolean
href?: string
iconOnly?: boolean
inline?: boolean
@@ -26,6 +29,7 @@ const props = withDefaults(
clear: false,
component: 'button',
disabled: false,
glass: false,
href: undefined,
iconOnly: false,
inline: false,
@@ -44,6 +48,23 @@ const emit = defineEmits<{
click: [event: MouseEvent]
}>()
const buttonClasses = computed(() => [
`sky-button--${props.variant}`,
{
'sky-button--block': props.block,
'sky-button--clear': props.clear,
'sky-button--glass': props.glass,
'sky-button--icon-only': props.iconOnly,
'sky-button--inline': props.inline,
'sky-button--large': props.large,
'sky-button--outline': props.outline,
'sky-button--raised': props.raised,
'sky-button--rounded': props.rounded,
'sky-button--small': props.small && !props.large,
'sky-button--tonal': props.tonal,
},
])
const elementProps = computed<Record<string, unknown>>(() => {
if (props.component === 'a') {
return {
@@ -71,25 +92,25 @@ function handleClick(event: MouseEvent): void {
</script>
<template>
<SkyGlass
v-if="glass"
:component="component"
v-bind="{ ...$attrs, ...elementProps }"
class="sky-button"
:class="buttonClasses"
:disabled="disabled"
:href="href"
:type="type"
@click="handleClick"
>
<slot />
</SkyGlass>
<component
v-else
:is="component"
v-bind="{ ...$attrs, ...elementProps }"
class="sky-button"
:class="[
`sky-button--${variant}`,
{
'sky-button--block': block,
'sky-button--clear': clear,
'sky-button--icon-only': iconOnly,
'sky-button--inline': inline,
'sky-button--large': large,
'sky-button--outline': outline,
'sky-button--raised': raised,
'sky-button--rounded': rounded,
'sky-button--small': small && !large,
'sky-button--tonal': tonal,
},
]"
:class="buttonClasses"
@click="handleClick"
>
<slot />
@@ -16,7 +16,45 @@ describe('PhoneApp EasyShare contract', () => {
it('uses the shared full-width Sky tab bar for phone sections', () => {
expect(source).toContain('<sky-tab-bar')
expect(source).toContain('<sky-tab-button')
expect(source).not.toContain('<sky-segmented')
})
it('uses shared interactive liquid glass surfaces for phone controls', () => {
expect(source).toMatch(
/<sky-button\s+glass\s+rounded\s+class="phone-detail-header-button phone-detail-back"/,
)
expect(source).toMatch(
/<sky-button\s+glass\s+v-for="action in contactProfileActions"[\s\S]*?icon-only[\s\S]*?class="phone-profile-action"/,
)
expect(source).toContain(
'width: var(--phone-profile-action-size) !important;',
)
expect(source).toContain(
'height: var(--phone-profile-action-size) !important;',
)
expect(source).toMatch(
/<sky-glass\s+v-for="key in keypadKeys"[\s\S]*?component="button"[\s\S]*?class="phone-keypad-key"/,
)
expect(source).toContain('class="phone-contacts-add"')
expect(source).toContain('class="phone-recents-search"')
const recentsFilter = source.slice(
source.indexOf('<sky-segmented'),
source.indexOf('</sky-segmented>') + '</sky-segmented>'.length,
)
expect(recentsFilter).toContain('class="phone-recents-filter"')
expect(recentsFilter).toContain('navigation')
expect(recentsFilter).toContain('strong')
expect(recentsFilter.match(/<sky-segmented-button/g)).toHaveLength(2)
expect(source).toContain(
'background: var(--sky-tabbar-highlight-background);',
)
expect(source).toContain(
'grid-template-columns: 52px minmax(0, 1fr) auto 44px;',
)
expect(source).not.toMatch(
/#(?:007aff|0a84ff|195287|22527d|25458e|2a468f|2f4a98|4b92d1|55aaff|5b91c2|64a8ff|68adff)/i,
)
expect(source).not.toContain('rgba(10, 132, 255')
})
it('opens contact deep links only after contacts bootstrap and consumes the query', () => {
File diff suppressed because it is too large Load Diff