Skip to content

Commit

Permalink
[feature] when default slot is not used, the tooltip will automatical…
Browse files Browse the repository at this point in the history
…ly center itself, becoming a step without highlighted elements.
f820602h committed Oct 5, 2023
1 parent 43face2 commit cfb0a06
Showing 14 changed files with 345 additions and 237 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ dist
dist-ssr
*.local
doc/.vitepress/cache/*
playground
coverage

# Editor directories and files
.vscode/*
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ import { StagePlayScene } from 'vue-stage-play'
</script>

<template>
<StagePlayScene actName="liveDemo" :scene="1">
<StagePlayScene :act-name="'liveDemo'" :scene="1">
<template #default="slotProp">
<div class="title">
<!-- ... -->
24 changes: 15 additions & 9 deletions doc/examples/basic-example.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<script setup>
import { ref } from 'vue'
import { StagePlaySpotlight, StagePlayScene, useStagePlay } from '../../src/index.ts'

const { action } = useStagePlay()

</script>

# Basic Example
@@ -12,7 +10,6 @@ This example demonstrates a guided tour, covering 「Activation with useStagePla

```vue
<script setup>
import { ref } from 'vue'
import { StagePlaySpotlight, StagePlayScene, useStagePlay } from 'vue-stage-play'
const { action } = useStagePlay()
@@ -24,7 +21,7 @@ const { action } = useStagePlay()
<button @click="action('basic')">Start</button>
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="1"
:voice-over-title="'Step1'"
:voice-over-content="'Open the door of the refrigerator.'"
@@ -33,7 +30,7 @@ const { action } = useStagePlay()
</StagePlayScene>
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="2"
:voice-over-title="'Step2'"
:voice-over-content="'Place the elephant inside the refrigerator.'"
@@ -42,13 +39,20 @@ const { action } = useStagePlay()
</StagePlayScene>
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="3"
:voice-over-title="'Step3'"
:voice-over-content="'Close the door of the refrigerator.'"
>
<h3>Step3</h3>
</StagePlayScene>
<StagePlayScene
:act-name="'basic'"
:scene="4"
:voice-over-title="'Success!'"
:voice-over-content="'You place an elephant into a refrigerator.'"
/>
</StagePlaySpotlight>
</template>
```
@@ -59,21 +63,23 @@ const { action } = useStagePlay()


<StagePlaySpotlight>
<StagePlayScene :actName="'basic'" :scene="1" :voice-over-title="'Step1'" :voice-over-content="'Open the door of the refrigerator.'" >
<StagePlayScene :act-name="'basic'" :scene="1" :voice-over-title="'Step1'" :voice-over-content="'Open the door of the refrigerator.'" >

### Step1

</StagePlayScene>

<StagePlayScene :actName="'basic'" :scene="2" :voice-over-title="'Step2'" :voice-over-content="'Place the elephant inside the refrigerator.'" >
<StagePlayScene :act-name="'basic'" :scene="2" :voice-over-title="'Step2'" :voice-over-content="'Place the elephant inside the refrigerator.'" >

### Step2

</StagePlayScene>

<StagePlayScene :actName="'basic'" :scene="3" :voice-over-title="'Step3'" :voice-over-content="'Close the door of the refrigerator.'" >
<StagePlayScene :act-name="'basic'" :scene="3" :voice-over-title="'Step3'" :voice-over-content="'Close the door of the refrigerator.'" >

### Step3

</StagePlayScene>

<StagePlayScene :act-name="'basic'" :scene="4" :voice-over-title="'Success!'" :voice-over-content="'You place an elephant into a refrigerator.'" />
</StagePlaySpotlight>
8 changes: 4 additions & 4 deletions doc/examples/event-hooks.md
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ function deactivated({ currentActName, currentScene}) {
<button @click="action('event')">Start</button>
<StagePlayScene
:actName="'event'"
:act-name="'event'"
:scene="1"
:voice-over-title="'Step1'"
:voice-over-content="'Open the door of the refrigerator.'"
@@ -84,19 +84,19 @@ function deactivated({ currentActName, currentScene}) {


<StagePlaySpotlight>
<StagePlayScene :actName="'event'" :scene="1" :voice-over-title="'Step1'" :voice-over-content="'Open the door of the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >
<StagePlayScene :act-name="'event'" :scene="1" :voice-over-title="'Step1'" :voice-over-content="'Open the door of the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >

### Step1

</StagePlayScene>

<StagePlayScene :actName="'event'" :scene="2" :voice-over-title="'Step2'" :voice-over-content="'Place the elephant inside the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >
<StagePlayScene :act-name="'event'" :scene="2" :voice-over-title="'Step2'" :voice-over-content="'Place the elephant inside the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >

### Step2

</StagePlayScene>

<StagePlayScene :actName="'event'" :scene="3" :voice-over-title="'Step3'" :voice-over-content="'Close the door of the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >
<StagePlayScene :act-name="'event'" :scene="3" :voice-over-title="'Step3'" :voice-over-content="'Close the door of the refrigerator.'" :on-before-cut="beforeCut" :on-after-cut="afterCut" :on-activated="activated" :on-deactivated="deactivated" >

### Step3

12 changes: 6 additions & 6 deletions doc/examples/nested-example.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import { StagePlaySpotlight, StagePlayScene } from 'vue-stage-play'
<template>
<StagePlaySpotlight>
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="1"
:voice-over-title="'Card'"
:voice-over-content="'This is a card.'"
@@ -23,7 +23,7 @@ import { StagePlaySpotlight, StagePlayScene } from 'vue-stage-play'
<template #default="scopedProps">
<div class="card">
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="2"
:voice-over-title="'Title'"
:voice-over-content="'This is title.'"
@@ -33,7 +33,7 @@ import { StagePlaySpotlight, StagePlayScene } from 'vue-stage-play'
</StagePlayScene>
<StagePlayScene
:actName="'basic'"
:act-name="'basic'"
:scene="3"
:voice-over-title="'Content'"
:voice-over-content="'This is content.'" :voice-over-placement="'right'"
@@ -52,19 +52,19 @@ import { StagePlaySpotlight, StagePlayScene } from 'vue-stage-play'
```

<StagePlaySpotlight>
<StagePlayScene style="display: flex; width: 300px" :actName="'basic'" :scene="1" :voice-over-title="'Card'" :voice-over-content="'This is a card.'" :voice-over-placement="'right'" >
<StagePlayScene style="display: flex; width: 300px" :act-name="'basic'" :scene="1" :voice-over-title="'Card'" :voice-over-content="'This is a card.'" :voice-over-placement="'right'" >

<template #default="scopedProps">

<div class="card" style="width: 300px; padding: 20px 20px 8px 20px; background: white; color: #292929; border-radius: 8px; box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;">

<StagePlayScene :actName="'basic'" :scene="2" :voice-over-title="'Title'" :voice-over-content="'This is title.'" :voice-over-placement="'right'" >
<StagePlayScene :act-name="'basic'" :scene="2" :voice-over-title="'Title'" :voice-over-content="'This is title.'" :voice-over-placement="'right'" >

<h3 style="margin: 0">Vue Stage Play</h3>

</StagePlayScene>

<StagePlayScene :actName="'basic'" :scene="3" :voice-over-title="'Content'" :voice-over-content="'This is content.'" :voice-over-placement="'right'" >
<StagePlayScene :act-name="'basic'" :scene="3" :voice-over-title="'Content'" :voice-over-content="'This is content.'" :voice-over-placement="'right'" >

<p>Designing a guided tour for your website with vue components, much like directing a stage play.</p>

69 changes: 55 additions & 14 deletions doc/examples/voice-over-customizing.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
<script setup>
import { ref } from 'vue'
import { StagePlaySpotlight, StagePlayScene, useStagePlay } from '../../src/index.ts'

const { action } = useStagePlay()

const placement = ref("top")
const align = ref("start")

</script>

# Voice Over Customizing
@@ -72,7 +67,6 @@ This example demonstrates the utilization of a customized tooltip.

```vue [App.vue]
<script setup>
import { ref } from 'vue'
import MyVoiceOver from "./components/MyVoiceOver"
import { StagePlaySpotlight, StagePlayScene, useStagePlay } from 'vue-stage-play'
@@ -84,7 +78,7 @@ const { action } = useStagePlay()
<h2>How to place an giraffe into a refrigerator?</h2>
<button @click="action('basic')">Start</button>
<StagePlayScene :actName="'voiceOverCustomizing'" :scene="1">
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="1">
<h3>Step1</h3>
<template #voiceOver="scopedProps">
@@ -100,7 +94,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>
<StagePlayScene :actName="'voiceOverCustomizing'" :scene="2">
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="2">
<h3>Step2</h3>
<template #voiceOver="scopedProps">
@@ -116,7 +110,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>
<StagePlayScene :actName="'voiceOverCustomizing'" :scene="3">
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="3">
<h3>Step3</h3>
<template #voiceOver="scopedProps">
@@ -132,7 +126,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>
<StagePlayScene :actName="'voiceOverCustomizing'" :scene="4">
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="4">
<h3>Step4</h3>
<template #voiceOver="scopedProps">
@@ -147,6 +141,20 @@ const { action } = useStagePlay()
/>
</template>
</StagePlayScene>
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="5">
<template #voiceOver="scopedProps">
<MyVoiceOver
:title="'Success!'"
:content="'You place an giraffe into a refrigerator.'"
:has-prev-scene="scopedProps.hasPrevScene"
:has-next-scene="scopedProps.hasNextScene"
@prev="scopedProps.prevScene()"
@next="scopedProps.nextScene()"
@done="scopedProps.cut()"
/>
</template>
</StagePlayScene>
</StagePlaySpotlight>
</template>
```
@@ -158,7 +166,7 @@ const { action } = useStagePlay()
<button class="btn" style="background: #34495e; color: white; border-radius: 4px; padding: 2px 12px" @click="action('voiceOverCustomizing')">Start</button>

<StagePlaySpotlight>
<StagePlayScene :actName="'voiceOverCustomizing'" :scene="1" >
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="1" >

### Step1

@@ -194,7 +202,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>

<StagePlayScene :actName="'voiceOverCustomizing'" :scene="2" >
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="2" >

### Step2

@@ -230,7 +238,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>

<StagePlayScene :actName="'voiceOverCustomizing'" :scene="3" >
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="3" >

### Step3

@@ -266,7 +274,7 @@ const { action } = useStagePlay()
</template>
</StagePlayScene>

<StagePlayScene :actName="'voiceOverCustomizing'" :scene="4" >
<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="4" >

### Step4

@@ -301,6 +309,39 @@ const { action } = useStagePlay()
</div>
</template>
</StagePlayScene>

<StagePlayScene :act-name="'voiceOverCustomizing'" :scene="5">
<template #voiceOver="scopedProps">
<div class="demo-card">
<button
class="btn"
:disabled="!scopedProps.hasPrevScene"
@click="scopedProps.prevScene()"
>
</button>
<div class="content">
<h3 class="title">Success!</h3>
<p>You place an giraffe into a refrigerator.</p>
</div>
<button
v-show="scopedProps.hasNextScene"
class="btn"
:disabled="!scopedProps.hasNextScene"
@click="scopedProps.nextScene()"
>
</button>
<button
v-show="!scopedProps.hasNextScene"
class="btn"
@click="scopedProps.cut()"
>
X
</button>
</div>
</template>
</StagePlayScene>
</StagePlaySpotlight>

<style scoped>
4 changes: 2 additions & 2 deletions doc/examples/voice-over-placement.md
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ const align = ref("start")
<button @click="action('voiceOverPlacement')">Start</button>
<StagePlayScene
:actName="'voiceOverPlacement'"
:act-name="'voiceOverPlacement'"
:scene="1"
:allow-interact="true"
:voice-over-auto-placement="false"
@@ -62,7 +62,7 @@ const align = ref("start")


<StagePlaySpotlight>
<StagePlayScene :actName="'voiceOverPlacement'" :scene="1" :allow-interact="true" :voice-over-auto-placement="false" :voice-over-placement="placement" :voice-over-align="align" >
<StagePlayScene :act-name="'voiceOverPlacement'" :scene="1" :allow-interact="true" :voice-over-auto-placement="false" :voice-over-placement="placement" :voice-over-align="align" >

<div style="display: flex; align-items: center; gap: 12px;">
<p>Placement:</p>
22 changes: 13 additions & 9 deletions doc/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<script setup>
import { ref } from 'vue'
import { StagePlaySpotlight } from '../src/components/StagePlaySpotlight.ts'
import { StagePlayScene } from '../src/components/StagePlayScene.ts'

const actName = ref("demo");

import { StagePlaySpotlight, StagePlayScene } from "../src/index.ts";
</script>


@@ -52,13 +47,13 @@ Next, import `<StagePlayScene>` where you want to highlight elements and wrap th

Set the `actName` and `scene` for `<StagePlayScene>`, and call the `action` function from the slot props.

``` vue{2,6,14,18}
``` vue{2,6,14,18,20-25}
<script setup lang="ts">
import { StagePlayScene } from 'vue-stage-play'
</script>
<template>
<StagePlayScene actName="liveDemo" :scene="1">
<StagePlayScene :act-name="'liveDemo'" :scene="1">
<template #default="slotProp">
<div class="title">
<!-- ... -->
@@ -71,15 +66,24 @@ import { StagePlayScene } from 'vue-stage-play'
</button>
</template>
</StagePlayScene>
<StagePlayScene
:act-name="'liveDemo'"
:scene="2"
:voice-over-title="'Only Vice Over'"
:voice-over-content="'You have the option not to highlight any elements.'"
/>
</template>
```

<StagePlayScene :actName="actName" :scene="1">
<StagePlayScene :act-name="'demo'" :scene="1">
<template #default="slotProp">

<button class="btn" style="background: #34495e; color: white; border-radius: 4px; padding: 2px 12px" @click="slotProp.action()">Live Demo</button>

</template>
</StagePlayScene>

<StagePlayScene :act-name="'demo'" :scene="2" :voice-over-title="'Only Vice Over'" :voice-over-content="'You have the option not to highlight any elements.'" />

</StagePlaySpotlight>
39 changes: 37 additions & 2 deletions doc/stage-play-scene.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { StagePlayScene } from 'vue-stage-play'
</script>
<template>
<StagePlayScene actName="demo" :scene="1">
<StagePlayScene :act-name="'demo'" :scene="1">
<template #default>
<div class="title">
<!-- ... -->
@@ -25,6 +25,26 @@ import { StagePlayScene } from 'vue-stage-play'
`<StagePlayScene>` will render an actual element. Hence, you may need to set some style to it, rather than the highlighted elements. For example, `margin`, `position`, and so forth.
:::


If necessary, you can choose not to highlight any elements. In this case, the voice-over will automatically be centered, becoming a standalone dialogue box.

``` vue{2,6-11}
<script setup lang="ts">
import { StagePlayScene } from 'vue-stage-play'
</script>
<template>
<StagePlayScene
:act-name="'demo'"
:scene="2"
:voice-title="'Only Vice Over'"
:voice-content="'You have the option not to highlight any elements.'"
/>
</template>
```



## Props

<script setup>
@@ -155,7 +175,7 @@ In all the slots described below, you can access slot props. ([see detail](#scop

``` vue{3,6}
<template>
<StagePlayScene actName="guide" :scene="1">
<StagePlayScene :act-name="'guide'" :scene="1">
<template #default="scopedProps">
<!-- ... -->
</template>
@@ -198,11 +218,14 @@ Slot for replacing the tooltip buttons.

| Property | Type |
| ------------------- | -------------------------------------------- |
| actName | `string` |
| scene | `number` |
| currentActName | `string \| undefined` |
| currentActSceneList | `number[]` |
| totalSceneCount | `number` |
| currentScene | `number \| undefined` |
| currentSceneOrder | `number` |
| currentActor | `HTMLElement \| null \| undefined` |
| hasPrevScene | `boolean` |
| hasNextScene | `boolean` |
| isCurrentScene | `boolean` |
@@ -212,6 +235,14 @@ Slot for replacing the tooltip buttons.
| nextScene | `() => void` |
| jumpToScene | `(scene: number) => void` |

### `actName`

The actName prop of this `<StagePlayScene>`.

### `scene`

The scene prop of this `<StagePlayScene>`.

### `currentActName`

The name of the act currently in progress.
@@ -232,6 +263,10 @@ The number of the scene currently in progress.

The order of the scene currently in progress.

### `currentActor`

The template ref of highlighting `<StagePlayScene>`.

### `isCurrentScene`

The boolean value indicating whether it is the current scene.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"name": "vue-stage-play",
"description": "Designing a guided tour for your website with vue components, much like directing a stage play.",
"private": false,
"version": "0.2.0",
"version": "0.3.0",
"author": "max.lee <f820602h@gmail.com>",
"license": "MIT",
"homepage": "https://github.com/f820602h/vue-stage-play#readme",
@@ -32,6 +32,7 @@
"build": "pkgroll --minify",
"lint": "eslint .",
"test": "vitest --dom --watch",
"play": "vite playground --open",
"coverage": "vitest --dom --coverage",
"typecheck": "vue-tsc --noEmit",
"docs:dev": "vitepress dev doc",
221 changes: 55 additions & 166 deletions src/components/StagePlayScene.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable vue/require-default-prop */
import type { PropType, StyleValue, SlotsType } from "vue";
import type { PropType, SlotsType } from "vue";
import {
h,
defineComponent,
@@ -22,6 +22,7 @@ import { useAct } from "../composables/act";
import { useBodyScrollFixed } from "../composables/bodyScrollFixed";
import { useFadeTransition } from "../composables/fade";
import { useWindowSize, useElementBounding } from "@vueuse/core";
import { smoothScroll, getPlacementStyle } from "../utils";

export const StagePlayScene = defineComponent({
slots: Object as SlotsType<{
@@ -144,6 +145,7 @@ export const StagePlayScene = defineComponent({
const spotlight = ref<HTMLElement | null>(null);
const voiceOver = ref<HTMLElement | null>(null);

const { fixed, reset } = useBodyScrollFixed();
const { enterTransition, leaveTransition } = useFadeTransition(250);

const globalOptions = inject(InjectionGlobalOptions, {});
@@ -184,6 +186,7 @@ export const StagePlayScene = defineComponent({
currentActSceneList,
currentScene,
currentSceneOrder,
currentActor,
hasPrevScene,
hasNextScene,
totalSceneCount,
@@ -204,11 +207,24 @@ export const StagePlayScene = defineComponent({
);
});

function action(actName?: string, scene?: number): void {
_action(actName || options.value.actName, scene);
}

async function cut(): Promise<void> {
await options.value.onBeforeCut(scopedProps);
_cut();
await options.value.onAfterCut(scopedProps);
}

const scopedProps: ScopedProps = reactive({
actName: options.value.actName,
scene: options.value.scene,
currentActName,
currentActSceneList,
currentScene,
currentSceneOrder,
currentActor,
hasPrevScene,
hasNextScene,
totalSceneCount,
@@ -220,30 +236,23 @@ export const StagePlayScene = defineComponent({
jumpToScene,
});

function action(actName?: string, scene?: number): void {
_action(actName || options.value.actName, scene);
}

async function cut(): Promise<void> {
await options.value.onBeforeCut(scopedProps);
_cut();
await options.value.onAfterCut(scopedProps);
}

const {
top: spotlightTop,
bottom: spotlightBottom,
left: spotlightLeft,
right: spotlightRight,
} = useElementBounding(spotlight);

const { width: voWidth, height: voHeight } = useElementBounding(voiceOver);

const { width: windowWidth, height: windowHeight } = useWindowSize();

const autoVoiceOverPlacement = computed<string>(() => {
if (!options.value.voiceOverAutoPlacement)
const voiceOverPlacement = computed<string>(() => {
if (slots.default === undefined) {
return "center";
}

if (!options.value.voiceOverAutoPlacement) {
return options.value.voiceOverPlacement;
}

let possiblePositions: string[] = [];
switch (options.value.voiceOverPlacement) {
@@ -303,34 +312,6 @@ export const StagePlayScene = defineComponent({
return possiblePositions[0] || options.value.voiceOverPlacement;
});

const { fixed, reset } = useBodyScrollFixed();

function smoothScroll(el: HTMLElement): Promise<void> {
return new Promise((resolve) => {
let same = 0;
let lastPos: number;

el.scrollIntoView(
options.value.cameraFollowOptions as ScrollIntoViewOptions,
);
requestAnimationFrame(check);

function check() {
const newPos = el.getBoundingClientRect().top;
if (newPos === lastPos) {
if (same++ > 2) {
return resolve();
}
} else {
same = 0;
lastPos = newPos;
}

requestAnimationFrame(check);
}
});
}

watch(
() => ({
actName: options.value.actName,
@@ -363,7 +344,10 @@ export const StagePlayScene = defineComponent({
options.value.cameraFollow &&
spotlight.value
) {
await smoothScroll(spotlight.value);
await smoothScroll(
spotlight.value,
options.value.cameraFollowOptions,
);
if (options.value.cameraFixAfterFollow) fixed();
}
}
@@ -374,122 +358,6 @@ export const StagePlayScene = defineComponent({
removeScene(options.value.actName, options.value.scene);
});

const voiceOverStyle = computed<StyleValue>(() => {
let top = "",
bottom = "",
left = "",
right = "",
transform = "";
switch (autoVoiceOverPlacement.value) {
case "top":
{
top = "0";
left = "0";
transform = "translate(0, -100%)";

switch (options.value.voiceOverAlign) {
case "start":
left = "0";
break;
case "center":
left = "50%";
transform = "translate(-50%, -100%)";
break;
case "end":
left = "unset";
right = "0";
break;
default:
break;
}
}
break;
case "bottom":
{
bottom = "0";
left = "0";
transform = "translate(0, 100%)";

switch (options.value.voiceOverAlign) {
case "start":
left = "0";
break;
case "center":
left = "50%";
transform = "translate(-50%, 100%)";
break;
case "end":
left = "unset";
right = "0";
break;
default:
break;
}
}
break;
case "left":
{
top = "0";
left = "0";
transform = "translate(-100%, 0)";

switch (options.value.voiceOverAlign) {
case "start":
top = "0";
break;
case "center":
top = "50%";
transform = "translate(-100%, -50%)";
break;
case "end":
top = "unset";
bottom = "0";
break;
default:
break;
}
}
break;
case "right":
{
top = "0";
right = "0";
transform = "translate(100%, 0)";

switch (options.value.voiceOverAlign) {
case "start":
top = "0";
break;
case "center":
top = "50%";
transform = "translate(100%, -50%)";
break;
case "end":
top = "unset";
bottom = "0";
break;
default:
break;
}
}
break;
default:
break;
}

return {
position: "absolute",
zIndex: "99996",
color: "#292929",
pointerEvents: "auto",
top,
bottom,
left,
right,
transform,
};
});

const isIconHover = ref(false);
const isPrevButtonHover = ref(false);
const isNextButtonHover = ref(false);
@@ -501,8 +369,11 @@ export const StagePlayScene = defineComponent({
{
class: "vue-stage-play__scene",
style: {
position: "relative",
position: slots.default ? "relative" : "fixed",
top: slots.default ? undefined : "50%",
left: slots.default ? undefined : "50%",
zIndex: isCurrentScene.value ? "99998" : "",
transform: slots.default ? undefined : "translate(-50%, -50%)",
},
},
[
@@ -516,10 +387,18 @@ export const StagePlayScene = defineComponent({
style: {
position: "absolute",
scrollMargin: `${options.value.cameraFollowOffset}px`,
top: `-${options.value.spotlightPadding || 0}px`,
bottom: `-${options.value.spotlightPadding || 0}px`,
left: `-${options.value.spotlightPadding || 0}px`,
right: `-${options.value.spotlightPadding || 0}px`,
top: slots.default
? `-${options.value.spotlightPadding || 0}px`
: "-1px",
bottom: slots.default
? `-${options.value.spotlightPadding || 0}px`
: "-1px",
left: slots.default
? `-${options.value.spotlightPadding || 0}px`
: "-1px",
right: slots.default
? `-${options.value.spotlightPadding || 0}px`
: "-1px",
pointerEvents:
isCurrentScene.value && !options.value.allowInteract
? undefined
@@ -540,7 +419,16 @@ export const StagePlayScene = defineComponent({
"div",
{
class: "vue-stage-play__voice-over",
style: voiceOverStyle.value,
style: {
position: "absolute",
zIndex: "99996",
color: "#292929",
pointerEvents: "auto",
...getPlacementStyle(
voiceOverPlacement.value,
options.value.voiceOverAlign,
),
},
ref: voiceOver,
},
[
@@ -586,6 +474,7 @@ export const StagePlayScene = defineComponent({
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
lineHeight: "1.5",
},
},
options.value.voiceOverTitle,
3 changes: 1 addition & 2 deletions src/composables/act.ts
Original file line number Diff line number Diff line change
@@ -176,13 +176,12 @@ export function useAct() {
acts,
currentActName,
currentActSceneList,
totalSceneCount,
currentScene,
currentSceneOrder,
currentActor,
hasPrevScene,
hasNextScene,
totalSceneCount,

action,
cut,
addScene,
147 changes: 147 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -31,3 +31,150 @@ export function findLastIndex<T>(
}
return -1;
}

export function smoothScroll(
el: HTMLElement,
options: ScrollIntoViewOptions,
): Promise<void> {
return new Promise((resolve) => {
let same = 0;
let lastPos: number;

el.scrollIntoView(options);
requestAnimationFrame(check);

function check() {
const newPos = el.getBoundingClientRect().top;
if (newPos === lastPos) {
if (same++ > 2) {
return resolve();
}
} else {
same = 0;
lastPos = newPos;
}

requestAnimationFrame(check);
}
});
}

export function getPlacementStyle(placement: string, align: string) {
let top = "",
bottom = "",
left = "",
right = "",
transform = "";

switch (placement) {
case "center":
{
top = "50%";
left = "50%";
transform = "translate(-50%, -50%)";
}
break;
case "top":
{
top = "0";
left = "0";
transform = "translate(0, -100%)";

switch (align) {
case "start":
left = "0";
break;
case "center":
left = "50%";
transform = "translate(-50%, -100%)";
break;
case "end":
left = "unset";
right = "0";
break;
default:
break;
}
}
break;
case "bottom":
{
bottom = "0";
left = "0";
transform = "translate(0, 100%)";

switch (align) {
case "start":
left = "0";
break;
case "center":
left = "50%";
transform = "translate(-50%, 100%)";
break;
case "end":
left = "unset";
right = "0";
break;
default:
break;
}
}
break;
case "left":
{
top = "0";
left = "0";
transform = "translate(-100%, 0)";

switch (align) {
case "start":
top = "0";
break;
case "center":
top = "50%";
transform = "translate(-100%, -50%)";
break;
case "end":
top = "unset";
bottom = "0";
break;
default:
break;
}
}
break;
case "right":
{
top = "0";
right = "0";
transform = "translate(100%, 0)";

switch (align) {
case "start":
top = "0";
break;
case "center":
top = "50%";
transform = "translate(100%, -50%)";
break;
case "end":
top = "unset";
bottom = "0";
break;
default:
break;
}
}
break;
default:
break;
}

return {
top,
bottom,
left,
right,
transform,
};
}
26 changes: 5 additions & 21 deletions tests/components/StagePlayScene.spec.ts
Original file line number Diff line number Diff line change
@@ -93,9 +93,6 @@ describe("StagePlayScene before action", () => {
expect(wrapper.attributes("style")).toBe("position: relative;");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: auto;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -138,9 +135,6 @@ describe("StagePlayScene before action", () => {
expect(wrapper.attributes("style")).toBe("position: relative;");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: auto;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -187,9 +181,6 @@ describe("StagePlayScene before action", () => {
expect(wrapper.attributes("style")).toBe("position: relative;");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: auto;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -255,9 +246,6 @@ describe("StagePlayScene after action", () => {
const clickMaskWrapper = wrapper.find(".vue-stage-play__click-mask");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: none;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -266,7 +254,7 @@ describe("StagePlayScene after action", () => {
)}`,
);
expect(spotlightWrapper.attributes("style")).toBe(
`position: absolute; scroll-margin: ${defaultOptions.cameraFollowOffset}px; top: -${defaultOptions.spotlightPadding}px; bottom: -${defaultOptions.spotlightPadding}px; left: -${defaultOptions.spotlightPadding}px; right: -${defaultOptions.spotlightPadding}px; pointer-events: none;`,
`position: absolute; scroll-margin: ${defaultOptions.cameraFollowOffset}px; top: -${defaultOptions.spotlightPadding}px; bottom: -${defaultOptions.spotlightPadding}px; left: -${defaultOptions.spotlightPadding}px; right: -${defaultOptions.spotlightPadding}px;`,
);

expect(voiceOverWrapper.exists()).toBe(true);
@@ -327,9 +315,6 @@ describe("StagePlayScene after action", () => {
const clickMaskWrapper = wrapper.find(".vue-stage-play__click-mask");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: none;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -338,7 +323,7 @@ describe("StagePlayScene after action", () => {
)}`,
);
expect(spotlightWrapper.attributes("style")).toBe(
`position: absolute; scroll-margin: ${mockGlobalOptions.cameraFollowOffset}px; top: -${mockGlobalOptions.spotlightPadding}px; bottom: -${mockGlobalOptions.spotlightPadding}px; left: -${mockGlobalOptions.spotlightPadding}px; right: -${mockGlobalOptions.spotlightPadding}px; pointer-events: none;`,
`position: absolute; scroll-margin: ${mockGlobalOptions.cameraFollowOffset}px; top: -${mockGlobalOptions.spotlightPadding}px; bottom: -${mockGlobalOptions.spotlightPadding}px; left: -${mockGlobalOptions.spotlightPadding}px; right: -${mockGlobalOptions.spotlightPadding}px;`,
);

expect(voiceOverWrapper.exists()).toBe(true);
@@ -411,9 +396,6 @@ describe("StagePlayScene after action", () => {
const clickMaskWrapper = wrapper.find(".vue-stage-play__click-mask");

expect(defaultSlotsWrapper.exists()).toBe(true);
expect(defaultSlotsWrapper.attributes("style")).toBe(
"pointer-events: none;",
);

expect(spotlightWrapper.exists()).toBe(true);
expect(spotlightWrapper.attributes("id")).toBe(
@@ -428,7 +410,7 @@ describe("StagePlayScene after action", () => {
mockGlobalOptions.spotlightPadding
}px; left: -${mockGlobalOptions.spotlightPadding}px; right: -${
mockGlobalOptions.spotlightPadding
}px; pointer-events: none;`,
}px;`,
);

expect(voiceOverWrapper.exists()).toBe(true);
@@ -580,6 +562,7 @@ describe("StagePlayScene after action", () => {

const doneButtonWrapper = wrapper.find(".default__voice-over__footer__btn");
doneButtonWrapper.trigger("click");
await nextTick();
expect(currentActName.value).toBeUndefined();
expect(currentScene.value).toBeUndefined();

@@ -616,6 +599,7 @@ describe("StagePlayScene after action", () => {

const clickMaskWrapper = wrapper.find(".vue-stage-play__click-mask");
clickMaskWrapper.trigger("click");
await nextTick();
expect(currentActName.value).toBeUndefined();
expect(currentScene.value).toBeUndefined();

0 comments on commit cfb0a06

Please sign in to comment.