Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lists #586

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Lists #586

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1349,4 +1349,48 @@ progress::-moz-progress-bar
100%
opacity 1

// Item Snap Guides

// waiting animations

@keyframes guideRightWaiting
0%
opacity 0
100%
transform translateX(2px)
opacity 0.75
@keyframes guideLeftWaiting
0%
opacity 0
100%
transform translateX(-2px)
opacity 0.75
@keyframes guideTopWaiting
0%
opacity 0
100%
transform translateY(-2px)
opacity 0.75
@keyframes guideBottomWaiting
0%
opacity 0
100%
transform translateY(2px)
opacity 0.75

// ready animations

@keyframes guideRightReady
50%
transform translateX(2px)
@keyframes guideLeftReady
50%
transform translateX(-2px)
@keyframes guideTopReady
50%
transform translateY(-2px)
@keyframes guideBottomReady
50%
transform translateY(2px)

</style>
2 changes: 2 additions & 0 deletions src/components/Box.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { reactive, computed, onMounted, onBeforeUnmount, onUpdated, onUnmounted, defineProps, defineEmits, watch, ref, nextTick } from 'vue'
import { useStore } from 'vuex'

import BoxSnapGuide from '@/components/BoxSnapGuide.vue'
import utils from '@/utils.js'
import consts from '@/consts.js'
import fonts from '@/data/fonts.js'
Expand Down Expand Up @@ -862,6 +863,7 @@ const isInCheckedBox = computed(() => {
tabindex="-1"
)
img.resize-icon.icon(src="@/assets/resize-corner.svg" :class="resizeColorClass")
BoxSnapGuide(:box="props.box")
</template>

<style lang="stylus">
Expand Down
84 changes: 10 additions & 74 deletions src/components/BoxSnapGuide.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ let unsubscribe
let waitingAnimationTimer, shouldCancelWaiting, waitingStartTime

onMounted(() => {
updateRect()
unsubscribe = store.subscribe(mutation => {
if (mutation.type === 'clearDraggingItems') {
cancelWaitingAnimationFrame()
Expand All @@ -28,7 +27,6 @@ const props = defineProps({
})

const state = reactive({
rect: null,
snapStatus: null // waiting, ready
})

Expand All @@ -51,9 +49,12 @@ const otherBoxes = computed(() => {
const userColor = computed(() => store.state.currentUser.color)
const currentBoxSnapGuide = computed(() => {
const isMultipleBoxesSelectedIds = store.state.multipleBoxesSelectedIds.length > 1
const cardSnapGuide = store.state.currentCards.snapGuide
if (isMultipleBoxesSelectedIds) { return }
if (cardSnapGuide) { return }
let guides = store.state.currentBoxes.snapGuides
return guides.find(guide => {
if (!guide) { return }
const isTarget = guide.target.id === props.box.id
const isOrigin = guide.origin.id === props.box.id
return isTarget || isOrigin
Expand All @@ -64,9 +65,9 @@ const snapGuideSide = computed(() => {
if (!isDraggingItem) { return }
const snapGuide = currentBoxSnapGuide.value
if (!snapGuide) { return null }
if (snapGuide?.target.id === props.box.id) {
if (snapGuide?.target?.id === props.box.id) {
return snapGuide.side
} else if (snapGuide?.origin.id === props.box.id) {
} else if (snapGuide?.origin?.id === props.box.id) {
return oppositeSide(snapGuide.side)
} else {
return null
Expand All @@ -86,35 +87,9 @@ const oppositeSide = (side) => {
return 'top'
}
}
const updateRect = () => {
state.rect = utils.boxElementDimensions({ id: props.box.id })
}
const snapGuideStyles = computed(() => {
const offset = 4
let styles = {}
let rect = state.rect
styles.background = props.box.color
// left
if (snapGuideSide.value === 'left') {
styles.height = rect.height + 'px'
styles.left = (rect.x - offset) + 'px'
styles.top = rect.y + 'px'
// right
} else if (snapGuideSide.value === 'right') {
styles.height = rect.height + 'px'
styles.left = (rect.x + rect.width - offset) + 'px'
styles.top = rect.y + 'px'
// top
} else if (snapGuideSide.value === 'top') {
styles.width = rect.width + 'px'
styles.left = rect.x + 'px'
styles.top = (rect.y - offset) + 'px'
// bottom
} else if (snapGuideSide.value === 'bottom') {
styles.width = rect.width + 'px'
styles.left = rect.x + 'px'
styles.top = (rect.y + rect.height - offset) + 'px'
}
if (snapGuideSide.value) {
startWaiting()
}
Expand Down Expand Up @@ -149,7 +124,6 @@ const waitingAnimationFrame = (timestamp) => {
}
const elaspedTime = timestamp - waitingStartTime
const percentComplete = (elaspedTime / consts.boxSnapGuideWaitingDuration) // between 0 and 1
updateRect()
// waiting
if (percentComplete < 1) {
state.snapStatus = 'waiting'
Expand All @@ -176,13 +150,14 @@ const waitingAnimationFrame = (timestamp) => {

<style lang="stylus">
.box-snap-guide
--snap-guide-width 6px
--snap-guide-width 8px
--snap-guide-waiting-duration 0.5s // same as consts.boxSnapGuideWaitingDuration ms
--snap-guide-ready-duration 0.4s
position absolute
&.left
left calc(-1 * var(--snap-guide-width))
width var(--snap-guide-width)
height 100%
border-top-left-radius var(--entity-radius)
border-bottom-left-radius var(--entity-radius)
&.waiting
Expand All @@ -192,6 +167,7 @@ const waitingAnimationFrame = (timestamp) => {
&.right
right calc(-1 * var(--snap-guide-width))
width var(--snap-guide-width)
height 100%
border-top-right-radius var(--entity-radius)
border-bottom-right-radius var(--entity-radius)
&.waiting
Expand All @@ -201,6 +177,7 @@ const waitingAnimationFrame = (timestamp) => {
&.top
top calc(-1 * var(--snap-guide-width))
height var(--snap-guide-width)
width 100%
border-top-left-radius var(--entity-radius)
border-top-right-radius var(--entity-radius)
&.waiting
Expand All @@ -210,52 +187,11 @@ const waitingAnimationFrame = (timestamp) => {
&.bottom
bottom calc(-1 * var(--snap-guide-width))
height var(--snap-guide-width)
width 100%
border-bottom-left-radius var(--entity-radius)
border-bottom-right-radius var(--entity-radius)
&.waiting
animation guideBottomWaiting var(--snap-guide-waiting-duration) 1 ease-in-out forwards
&.ready
animation guideBottomReady var(--snap-guide-ready-duration) infinite ease-in-out forwards

// waiting animations

@keyframes guideRightWaiting
0%
opacity 0
100%
transform translateX(2px)
opacity 0.75
@keyframes guideLeftWaiting
0%
opacity 0
100%
transform translateX(-2px)
opacity 0.75
@keyframes guideTopWaiting
0%
opacity 0
100%
transform translateY(-2px)
opacity 0.75
@keyframes guideBottomWaiting
0%
opacity 0
100%
transform translateY(2px)
opacity 0.75

// ready animations

@keyframes guideRightReady
50%
transform translateX(2px)
@keyframes guideLeftReady
50%
transform translateX(-2px)
@keyframes guideTopReady
50%
transform translateY(-2px)
@keyframes guideBottomReady
50%
transform translateY(2px)
</style>
2 changes: 0 additions & 2 deletions src/components/Boxes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { reactive, computed, onMounted, onBeforeUnmount, defineProps, defineEmit
import { useStore } from 'vuex'

import Box from '@/components/Box.vue'
import BoxSnapGuide from '@/components/BoxSnapGuide.vue'
const store = useStore()

const isPainting = computed(() => store.state.currentUserIsPainting)
Expand All @@ -16,7 +15,6 @@ const spaceZoomDecimal = computed(() => store.getters.spaceZoomDecimal)
//- locked boxes rendered in ItemsLocked
template(v-for="box in unlockedBoxes")
Box(:box="box")
BoxSnapGuide(:box="box")
</template>

<style lang="stylus">
Expand Down
2 changes: 2 additions & 0 deletions src/components/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import UserLabelInline from '@/components/UserLabelInline.vue'
import OtherCardPreview from '@/components/OtherCardPreview.vue'
import GroupInvitePreview from '@/components/GroupInvitePreview.vue'
import ItemConnectorButton from '@/components/ItemConnectorButton.vue'
import CardSnapGuide from '@/components/CardSnapGuide.vue'
import consts from '@/consts.js'
import postMessage from '@/postMessage.js'

Expand Down Expand Up @@ -2101,6 +2102,7 @@ article.card-wrap#card(
img.icon.time(src="@/assets/time.svg")
.name {{dateUpdatedAt}}

CardSnapGuide(:card="card")
</template>

<style lang="stylus">
Expand Down
2 changes: 1 addition & 1 deletion src/components/CardList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ span
//- date
span.badge.status.inline-badge(:class="{'date-is-today': dateIsToday(card)}")
img.icon.time(src="@/assets/time.svg")
span {{ relativeDate(card) }}
span(v-if="cardDate(card)") {{ relativeDate(card) }}
//- user
UserLabelInline(v-if="card.user.id && userIsNotCurrentUser(card.user.id)" :user="card.user")
//- name
Expand Down
109 changes: 109 additions & 0 deletions src/components/CardSnapGuide.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<script setup>
import { reactive, computed, onMounted, onBeforeUnmount, defineProps, defineEmits, watch, ref, nextTick } from 'vue'
import { useStore } from 'vuex'

import utils from '@/utils.js'
import consts from '@/consts.js'

const store = useStore()

const props = defineProps({
card: Object
})

const state = reactive({
snapStatus: 'ready'
})

const currentCardIsSelected = computed(() => {
const selected = store.state.multipleCardsSelectedIds
return selected.find(id => props.card.id === id)
})
const currentCardIsBeingDragged = computed(() => {
const isDragging = store.state.currentUserIsDraggingCard
const isCurrent = store.state.currentDraggingCardId === props.card.id
return isDragging && (isCurrent || currentCardIsSelected.value)
})
const otherCards = computed(() => {
const cards = store.getters['currentCards/isSelectableInViewport']
return cards.filter(card => card?.id !== props.card.id)
})

// styles

const userColor = computed(() => store.state.currentUser.color)
const currentCardSnapGuide = computed(() => {
let snapGuide = store.state.currentCards.snapGuide
if (!snapGuide) { return }
const isTarget = snapGuide.target.id === props.card.id
const isOrigin = snapGuide.origin.id === props.card.id
if (isTarget || isOrigin) {
return snapGuide
} else {
return null
}
})
const snapGuideSide = computed(() => {
const isDraggingItem = store.state.currentUserIsDraggingCard
if (!isDraggingItem) { return }
const snapGuide = currentCardSnapGuide.value
if (!snapGuide) { return null }
if (snapGuide?.target?.id === props.card.id) {
return snapGuide.side
} else if (snapGuide?.origin?.id === props.card.id) {
return oppositeSide(snapGuide.side)
} else {
return null
}
})
const oppositeSide = (side) => {
if (side === 'top') {
return 'bottom'
}
if (side === 'bottom') {
return 'top'
}
}
const snapGuideStyles = computed(() => {
let styles = {}
// TODO color should be current list color, or new list color
styles.background = currentCardSnapGuide.value.color
return styles
})
</script>

<template lang="pug">
.card-snap-guide.top(v-if="snapGuideSide === 'top'" :style="snapGuideStyles" :class="state.snapStatus")
.card-snap-guide.bottom(v-if="snapGuideSide === 'bottom'" :style="snapGuideStyles" :class="state.snapStatus")
</template>

<style lang="stylus">
.card-snap-guide
--snap-guide-width 8px
--snap-guide-ready-duration 0.4s
position absolute
&.top
top calc(-1 * var(--snap-guide-width))
height var(--snap-guide-width)
width 100%
border-top-left-radius var(--entity-radius)
border-top-right-radius var(--entity-radius)
&.ready
animation guideTopReady var(--snap-guide-ready-duration) infinite ease-in-out forwards
&.bottom
bottom calc(-1 * var(--snap-guide-width))
height var(--snap-guide-width)
width 100%
border-bottom-left-radius var(--entity-radius)
border-bottom-right-radius var(--entity-radius)
&.ready
animation guideBottomReady var(--snap-guide-ready-duration) infinite ease-in-out forwards

@keyframes guideTopReady
50%
transform translateY(2px)
@keyframes guideBottomReady
50%
transform translateY(-2px)

</style>
2 changes: 1 addition & 1 deletion src/components/CardsCreatedProgress.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const state = reactive({
freeLimitFAQIsVisible: false
})

const cardsCreatedCount = computed(() => store.state.currentUser.cardsCreatedCount || 0)
const cardsCreatedCount = computed(() => Math.min(0, store.state.currentUser.cardsCreatedCount || 0))
const cardsCreatedLimit = computed(() => store.state.cardsCreatedLimit)

const triggerUpgradeUserIsVisible = () => {
Expand Down
Loading