Skip to content

Commit

Permalink
feat(web): selects meshes by clicking on their size/quantity indicator (
Browse files Browse the repository at this point in the history
  • Loading branch information
feugy committed Dec 17, 2022
1 parent 7163f32 commit ad79e64
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 18 deletions.
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ AssertionError: expected 2nd "spy" call to have been called with [ 'anima' ]
- detailable/stackable behavior: preview a stack of meshes
- hide/distinguish non-connected participants
- is this right? given an active selection, when it anchors with other items, then items are part of the selection
- click on stack size to select all/select stack on push?
- option to invite players with url
- distribute multiple meshes to players' hand
- shortcuts cheatsheet
Expand Down
14 changes: 12 additions & 2 deletions apps/web/src/components/Label.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
<script>
export let content
export let onClick
export let screenPosition = { x: 0, y: 0 }
export let color = '#7a7a7a'
export let centered = false
function interact() {
onClick?.()
}
</script>
<div
class:centered
style="top: {screenPosition.y}px; left: {screenPosition.x}px; --color:{color}"
style="top: {screenPosition.y}px; left: {screenPosition.x}px; --color:{color}; pointer-events:{onClick
? 'auto'
: 'none'};"
on:click={interact}
on:keydown={interact}
on:pointerdown|stopPropagation
>
{content}
</div>
<style lang="postcss">
div {
@apply absolute transform-gpu -translate-y-1/2 -translate-x-1
pointer-events-none
select-none
text-lg text-$primary-lightest opacity-85
py-1 pr-2 pl-5 rounded z-10;
clip-path: polygon(1rem 0%, 100% 0, 100% 100%, 1rem 100%, 0% 50%);
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/routes/(auth)/game/[gameId]/GameHand.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
})
</script>
<aside class:highlight class:visible style="--color: {color}">
<aside
class:highlight
class:visible
style="--color: {color}"
on:pointerdown|stopPropagation
>
<MinimizableSection
placement="bottom"
tabs={[{ icon: 'front_hand', key: $_('shortcuts.hand') }]}
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/routes/(auth)/game/[gameId]/Indicators.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
}
</script>
{#each items as { screenPosition, id, mesh, player, ...rest } ({ id, screenPosition })}
{#each items as { screenPosition, id, mesh, player, onClick, ...rest } ({ id, screenPosition })}
{#if mesh}
<Label
{screenPosition}
{onClick}
centered={!!player}
content={computeLabel(player, rest)}
color={player?.color}
Expand Down
14 changes: 13 additions & 1 deletion apps/web/src/stores/indicators.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BehaviorSubject, map, merge, of, withLatestFrom } from 'rxjs'

import { selectionManager } from '../3d/managers'
import { getPixelDimension, observeDimension } from '../utils/dom'
import {
actionMenuProps,
Expand Down Expand Up @@ -79,7 +80,8 @@ export const visibleIndicators = merge(
handPosition
)
),
map(enrichWithPlayerData)
map(enrichWithPlayerData),
map(enrichWithInteraction)
)

/**
Expand Down Expand Up @@ -134,6 +136,16 @@ function enrichWithPlayerData(indicators) {
})
}

function enrichWithInteraction(indicators) {
return indicators.map(indicator => ({
...indicator,
onClick:
indicator.mesh && !selectionManager.meshes.has(indicator.mesh)
? () => selectionManager.select([indicator.mesh])
: null
}))
}

function memorizeAndMergeFeedback(indicators) {
const results = []
const now = Date.now()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
import { Tool, ToolBox } from '@atelier-wb/svelte'
import { recordEvent, Tool, ToolBox } from '@atelier-wb/svelte'
import Indicators from '@src/routes/(auth)/game/[gameId]/Indicators.svelte'
</script>

Expand All @@ -12,7 +12,13 @@
name="Single"
props={{
items: [
{ screenPosition: { x: 50, y: 100 }, size: 5, id: 'card1', mesh: {} }
{
screenPosition: { x: 50, y: 100 },
size: 5,
id: 'card1',
mesh: {},
onClick: () => recordEvent('select')
}
]
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ exports[`All kinds 1`] = `
HTMLCollection [
<div
class="s-WFVnM7df3-nu centered"
style="top: 125px; left: 150px; --color: cyan;"
style="top: 125px; left: 150px; --color: cyan; pointer-events: none;"
>
Gaspard
</div>,
<div
class="s-WFVnM7df3-nu"
style="top: 140px; left: 150px; --color: #7a7a7a;"
style="top: 140px; left: 150px; --color: #7a7a7a; pointer-events: none;"
>
3
</div>,
Expand Down Expand Up @@ -88,7 +88,7 @@ exports[`Feedback for player 1`] = `
exports[`For player 1`] = `
<div
class="s-WFVnM7df3-nu centered"
style="top: 125px; left: 150px; --color: #967d3e;"
style="top: 125px; left: 150px; --color: #967d3e; pointer-events: none;"
>
Gaspard
</div>
Expand All @@ -98,13 +98,13 @@ exports[`Multiple 1`] = `
HTMLCollection [
<div
class="s-WFVnM7df3-nu"
style="top: 100px; left: 50px; --color: #7a7a7a;"
style="top: 100px; left: 50px; --color: #7a7a7a; pointer-events: none;"
>
5
</div>,
<div
class="s-WFVnM7df3-nu"
style="top: 200px; left: 150px; --color: #7a7a7a;"
style="top: 200px; left: 150px; --color: #7a7a7a; pointer-events: none;"
>
27
</div>,
Expand Down Expand Up @@ -135,7 +135,7 @@ exports[`Peer pointer 1`] = `
exports[`Single 1`] = `
<div
class="s-WFVnM7df3-nu"
style="top: 100px; left: 50px; --color: #7a7a7a;"
style="top: 100px; left: 50px; --color: #7a7a7a; pointer-events: auto;"
>
5
</div>
Expand Down
58 changes: 54 additions & 4 deletions apps/web/tests/stores/indicators.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { faker } from '@faker-js/faker'
import { AnchorBehaviorName, StackBehaviorName } from '@src/3d/behaviors'
import {
AnchorBehaviorName,
QuantityBehaviorName,
StackBehaviorName
} from '@src/3d/behaviors'
import { indicatorManager } from '@src/3d/managers/indicator'
import { selectionManager } from '@src/3d/managers/selection'
import { createCard } from '@src/3d/meshes'
Expand Down Expand Up @@ -88,7 +92,9 @@ describe('Indicators store', () => {
{ id: 'card4', x: 5 },
{ id: 'card5', x: -5 },
{ id: 'card6', z: 5 }
].map(params => createCard({ ...params, stackable: {}, anchorable: {} }))
].map(params =>
createCard({ ...params, stackable: {}, anchorable: {}, quantifiable: {} })
)
actionMenuProps.next(null)
selectionManager.clear()
})
Expand All @@ -105,6 +111,31 @@ describe('Indicators store', () => {
})

it('has indicators for each stackable mesh', async () => {
const [card1, card2, card3, , card5] = cards
card1
.getBehaviorByName(StackBehaviorName)
.fromState({ stackIds: ['card4', 'card5'] })
card2.getBehaviorByName(QuantityBehaviorName).fromState({ quantity: 5 })
card3.getBehaviorByName(QuantityBehaviorName).fromState({ quantity: 1 })

await waitNextRender(scene)
expectIndicators([
{
id: `${card5.id}.stack-size`,
size: 3,
screenPosition: { x: 1024, y: 511.8 },
onClick: expect.any(Function)
},
{
id: `${card2.id}.quantity`,
size: 5,
screenPosition: { x: 1048.22, y: 512 },
onClick: expect.any(Function)
}
])
})

it('has indicators for quantifiable meshes with quantity higher than 1', async () => {
const [card1, , card3, card4, card5] = cards
card1
.getBehaviorByName(StackBehaviorName)
Expand All @@ -118,16 +149,35 @@ describe('Indicators store', () => {
{
id: `${card4.id}.stack-size`,
size: 3,
screenPosition: { x: 1024, y: 511.8 }
screenPosition: { x: 1024, y: 511.8 },
onClick: expect.any(Function)
},
{
id: `${card5.id}.stack-size`,
size: 2,
screenPosition: { x: 999.78, y: 511.9 }
screenPosition: { x: 999.78, y: 511.9 },
onClick: expect.any(Function)
}
])
})

it('selects mesh when interacting with their indicators, and removes interactivity', async () => {
cards[0]
.getBehaviorByName(StackBehaviorName)
.fromState({ stackIds: ['card2', 'card4'] })
await waitNextRender(scene)

get(visibleIndicators$)[0].onClick()
expect(selectionManager.meshes.has(cards[0])).toBe(true)
expect(selectionManager.meshes.has(cards[1])).toBe(true)
expect(selectionManager.meshes.has(cards[2])).toBe(false)
expect(selectionManager.meshes.has(cards[3])).toBe(true)
expect(selectionManager.meshes.has(cards[4])).toBe(false)

await waitNextRender(scene)
expect(get(visibleIndicators$)[0]).toHaveProperty('onClick', null)
})

it('has feedback', async () => {
const indicator = { position: [1, 0, -1], isFeedback: true }
indicatorManager.registerFeedback(indicator)
Expand Down

2 comments on commit ad79e64

@vercel
Copy link

@vercel vercel bot commented on ad79e64 Dec 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

tabulous-atelier – ./apps/web

tabulous-atelier-git-main-feugy.vercel.app
tabulous-atelier-feugy.vercel.app
tabulous-atelier.vercel.app

@vercel
Copy link

@vercel vercel bot commented on ad79e64 Dec 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

tabulous – ./apps/web

tabulous-feugy.vercel.app
tabulous-git-main-feugy.vercel.app
tabulous.vercel.app
tabulous.fr

Please sign in to comment.