feat: integrate accessible journey world and controls

This commit is contained in:
2026-09-02 05:07:24 +03:00
parent fd8347fbdc
commit 190ac1ce62
10 changed files with 755 additions and 15 deletions

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { MotionPolicy, QualityTier } from '@/journey/motion-policy';
const props = defineProps<{
policy: MotionPolicy;
maximumQuality: QualityTier;
sceneStatus: string;
}>();
const emit = defineEmits<{
pause: [];
resume: [];
enable: [];
qualityChange: [quality: QualityTier];
}>();
const action = computed(() => {
if (props.policy.reason === 'visitor-pause') return { label: 'Resume motion', event: 'resume' as const };
if (props.policy.reason === 'saved-static') return { label: 'Enable motion', event: 'enable' as const };
if (props.policy.reason === 'full') return { label: 'Pause motion', event: 'pause' as const };
return null;
});
const statusText = computed(() => {
if (props.policy.reason === 'system-reduce') return 'Motion: static — reduced motion requested';
if (props.policy.reason === 'data-saver') return 'Motion: static — data saver is active';
if (props.policy.reason === 'visitor-pause') return 'Motion: paused';
if (props.policy.reason === 'saved-static') return 'Motion: static';
if (props.sceneStatus === 'failed' || props.sceneStatus === 'static') return 'Motion: static — scene unavailable';
if (props.sceneStatus === 'loading' || props.sceneStatus === 'idle') return 'Motion: preparing';
return 'Motion: active';
});
const runAction = (): void => {
const event = action.value?.event;
if (event === 'pause') emit('pause');
else if (event === 'resume') emit('resume');
else if (event === 'enable') emit('enable');
};
const changeQuality = (event: Event): void => {
const value = (event.currentTarget as HTMLSelectElement).value;
if (value === 'high' || value === 'medium' || value === 'low' || value === 'static') {
emit('qualityChange', value);
}
};
</script>
<template>
<div class="drive-controls">
<button v-if="action" type="button" @click="runAction">{{ action.label }}</button>
<label>
<span>Quality</span>
<select :value="maximumQuality" aria-label="Quality control" @change="changeQuality">
<option value="high">High</option>
<option value="medium">Medium</option>
<option value="low">Low</option>
<option value="static">Static</option>
</select>
</label>
<p class="drive-controls__status" role="status">{{ statusText }}</p>
</div>
</template>