Skip to content

Commit

Permalink
perf: grid layout
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Apr 4, 2022
1 parent 24f510b commit 218f856
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 64 deletions.
1 change: 0 additions & 1 deletion packages/histoire/package.json
Expand Up @@ -68,7 +68,6 @@
"sirv": "^2.0.2",
"tinypool": "^0.1.2",
"vite-node": "=0.7.6",
"vue-observe-visibility": "^2.0.0-alpha.1",
"vue-router": "^4.0.12"
},
"peerDependencies": {
Expand Down
103 changes: 103 additions & 0 deletions packages/histoire/src/client/app/components/story/StoryVariantGrid.vue
@@ -0,0 +1,103 @@
<script lang="ts" setup>
import { useResizeObserver } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useStoryStore } from '../../stores/story'
import StoryVariantGridItem from './StoryVariantGridItem.vue'
const storyStore = useStoryStore()
const templateWidth = computed(() => {
if (storyStore.currentStory.layout.type !== 'grid') {
return
}
const layoutWidth = storyStore.currentStory.layout.width
if (!layoutWidth) {
return '200px'
}
if (typeof layoutWidth === 'number') {
return layoutWidth + 'px'
}
return layoutWidth
})
const margin = 16
const gap = 16
const itemWidth = ref(16)
const maxItemHeight = ref(0)
const maxCount = ref(10)
const countPerRow = ref(0)
const visibleRows = ref(0)
const el = ref<HTMLDivElement>(null)
const children = ref([])
useResizeObserver(el, () => {
updateMaxCount()
})
function updateMaxCount () {
if (!maxItemHeight.value) return
const width = el.value!.clientWidth - margin * 2
const height = el.value!.clientHeight
const scrollTop = el.value!.scrollTop
// width = (countPerRow - 1) * gap + countPerRow * itemWidth.value
// W = (C - 1) * G + C * I
// W = C * G - G + C * I
// W + G = C * G + C * I
// W + G = C * (G + I)
// (W + G) / (G + I) = C
countPerRow.value = Math.floor((width + gap) / (itemWidth.value + gap))
visibleRows.value = Math.ceil((height + scrollTop + gap) / (maxItemHeight.value + gap))
const newMaxCount = countPerRow.value * visibleRows.value
if (maxCount.value < newMaxCount) {
maxCount.value = newMaxCount
}
console.log(countPerRow.value, visibleRows.value, maxCount.value)
}
function onItemResize (w: number, h: number) {
itemWidth.value = w
if (maxItemHeight.value < h) {
maxItemHeight.value = h
updateMaxCount()
}
}
</script>

<template>
<div
ref="el"
class="htw-h-full htw-overflow-y-auto"
@scroll="updateMaxCount()"
>
<div
:style="{
minHeight: `${(storyStore.currentStory.variants.length / countPerRow) * (maxItemHeight + gap) - gap}px`,
}"
>
<div
class="htw-grid htw-gap-4 htw-m-4"
:style="{
gridTemplateColumns: `repeat(auto-fill, ${templateWidth})`,
}"
>
<StoryVariantGridItem
v-for="(variant, index) of storyStore.currentStory.variants.slice(0, maxCount)"
:key="index"
:variant="variant"
:story="storyStore.currentStory"
@resize="onItemResize"
/>
</div>
</div>
</div>
</template>
Expand Up @@ -2,7 +2,7 @@
import { PropType, ref, toRefs } from 'vue'
import { Icon } from '@iconify/vue'
import { useRouter } from 'vue-router'
import { ObserveVisibility as vObserveVisibility } from 'vue-observe-visibility'
import { useResizeObserver } from '@vueuse/core'
import { useCurrentVariantRoute } from '../../util/variant'
import type { Story, Variant } from '../../types'
import SandboxVue3 from '../sandbox/SandboxVue3.vue'
Expand All @@ -19,6 +19,10 @@ const props = defineProps({
},
})
const emit = defineEmits({
resize: (width: number, height: number) => true,
})
const { variant } = toRefs(props)
const { isActive, targetRoute } = useCurrentVariantRoute(variant)
Expand All @@ -37,17 +41,17 @@ function selectVariant () {
router.push(targetRoute.value)
}
const lazyMount = ref(false)
function visibilityChanged (isVisible) {
if (isVisible) {
lazyMount.value = true
const el = ref<HTMLDivElement>()
useResizeObserver(el, () => {
if (props.variant.ready) {
emit('resize', el.value!.clientWidth, el.value!.clientHeight)
}
}
})
</script>

<template>
<div
v-observe-visibility="visibilityChanged"
ref="el"
class="htw-cursor-default htw-flex htw-flex-col htw-gap-y-1"
>
<!-- Header -->
Expand All @@ -74,7 +78,6 @@ function visibilityChanged (isVisible) {
<!-- Body -->
<div
v-if="lazyMount"
class="htw-border htw-bg-white dark:htw-bg-gray-700 htw-rounded-lg htw-flex-1 htw-p-4"
:class="{
'htw-border-gray-100 dark:htw-border-gray-800': !isActive,
Expand Down
47 changes: 5 additions & 42 deletions packages/histoire/src/client/app/components/story/StoryViewer.vue
@@ -1,21 +1,16 @@
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useStoryStore } from '../../stores/story'
import { Icon } from '@iconify/vue'
import { isMobile } from '../../util/responsive'
import BaseSplitPane from '../base/BaseSplitPane.vue'
import StoryVariantListItem from './StoryVariantListItem.vue'
import StoryVariantGridItem from './StoryVariantGridItem.vue'
import StoryVariantSingleView from './StoryVariantSingleView.vue'
import { isMobile } from '../../util/responsive'
import { Icon } from '@iconify/vue'
import MobileOverlay from '../app/MobileOverlay.vue'
import StoryVariantGrid from './StoryVariantGrid.vue'
const storyStore = useStoryStore()
const router = useRouter()
const route = useRoute()
const hasSingleVariant = computed(() => (storyStore.currentStory?.variants.length === 1))
const variant = computed(() => storyStore.currentVariant)
Expand All @@ -34,45 +29,13 @@ watch(variant, () => {
isMenuOpened.value = false
})
const templateWidth = computed(() => {
if (storyStore.currentStory.layout.type !== 'grid') {
return
}
const layoutWidth = storyStore.currentStory.layout.width
if (!layoutWidth) {
return '200px'
}
if (typeof layoutWidth === 'number') {
return layoutWidth + 'px'
}
return layoutWidth
})
</script>

<template>
<div class="htw-bg-gray-50 htw-h-full dark:htw-bg-gray-750">
<div
<StoryVariantGrid
v-if="storyStore.currentStory.layout.type === 'grid'"
class="htw-h-full htw-overflow-y-auto"
>
<div
class="htw-grid htw-gap-4 htw-m-4"
:style="{
gridTemplateColumns: `repeat(auto-fill, ${templateWidth})`,
}"
>
<StoryVariantGridItem
v-for="(variant, index) of storyStore.currentStory.variants"
:key="index"
:variant="variant"
:story="storyStore.currentStory"
/>
</div>
</div>
/>

<template v-else-if="storyStore.currentStory.layout.type === 'single'">
<div
Expand Down
13 changes: 0 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 218f856

Please sign in to comment.