Skip to content

Commit

Permalink
feat: Preview snap (fix #54) (#65)
Browse files Browse the repository at this point in the history
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com>
  • Loading branch information
hugoattal and Akryum committed Apr 10, 2022
1 parent 87126ff commit c53cc5c
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@ import { computed, onUnmounted, Ref, ref, toRaw, watch } from 'vue'
import { useEventListener } from '@vueuse/core'
import { Icon } from '@iconify/vue'
import { STATE_SYNC, PREVIEW_SETTINGS_SYNC, SANDBOX_READY } from '../../util/const'
import { PreviewSettings } from '../../util/preview-settings'
import type { Story, Variant } from '../../types'
import HatchedPattern from '../misc/HatchedPattern.vue'
import CheckerboardPattern from '../misc/CheckerboardPattern.vue'
import { toRawDeep } from '../../util/reactivity'
import { Settings } from 'http2'
import { getSandboxUrl } from '../sandbox/lib'
import { usePreviewSettingsStore } from '../../stores/preview-settings'
const props = defineProps<{
story: Story
variant: Variant
settings: PreviewSettings
}>()
const emit = defineEmits({
'update:settings': (settings: PreviewSettings) => true,
})
const settings = usePreviewSettingsStore().currentSettings
// Iframe
Expand Down Expand Up @@ -77,12 +73,12 @@ function syncSettings () {
if (iframe.value) {
iframe.value.contentWindow.postMessage({
type: PREVIEW_SETTINGS_SYNC,
settings: toRaw(props.settings),
settings: toRaw(settings),
})
}
}
watch(() => props.settings, value => {
watch(() => settings, () => {
syncSettings()
}, {
deep: true,
Expand Down Expand Up @@ -130,8 +126,13 @@ function useDragger (el: Ref<HTMLDivElement>, value: Ref<number>, min: number, m
]
function onMouseMove (event: MouseEvent) {
const snapTarget = (axis === 'x' ? previewWrapper.value.clientWidth : previewWrapper.value.clientHeight)
const delta = (axis === 'x' ? event.clientX : event.clientY) - start
value.value = Math.max(min, Math.min(max, startValue + delta))
if (Math.abs(value.value - (snapTarget - 67)) < 16) {
value.value = null
}
}
function onMouseUp () {
Expand Down Expand Up @@ -167,57 +168,29 @@ function useDragger (el: Ref<HTMLDivElement>, value: Ref<number>, min: number, m
useEventListener(el, 'touchstart', onTouchStart)
}
// Optimize by batching settings updates to the next frame
// Prevents sync issues with `useStorage`
let settingsUpdate: Partial<Settings> = {}
let settingsUpdateQueued = false
function updateSettings (settings: Partial<PreviewSettings>) {
Object.assign(settingsUpdate, settings)
if (!settingsUpdateQueued) {
settingsUpdateQueued = true
requestAnimationFrame(() => {
emit('update:settings', {
...props.settings,
...settingsUpdate,
})
settingsUpdate = {}
settingsUpdateQueued = false
})
}
}
const responsiveWidth = computed({
get: () => props.settings[props.settings.rotate ? 'responsiveHeight' : 'responsiveWidth'],
set: (value) => {
updateSettings({
[props.settings.rotate ? 'responsiveHeight' : 'responsiveWidth']: value,
})
},
get: () => settings[settings.rotate ? 'responsiveHeight' : 'responsiveWidth'],
set: (value) => { settings[settings.rotate ? 'responsiveHeight' : 'responsiveWidth'] = value },
})
const responsiveHeight = computed({
get: () => props.settings[props.settings.rotate ? 'responsiveWidth' : 'responsiveHeight'],
set: (value) => {
updateSettings({
[props.settings.rotate ? 'responsiveWidth' : 'responsiveHeight']: value,
})
},
get: () => settings[settings.rotate ? 'responsiveWidth' : 'responsiveHeight'],
set: (value) => { settings[settings.rotate ? 'responsiveWidth' : 'responsiveHeight'] = value },
})
const horizontalDragger = ref<HTMLDivElement>()
const verticalDragger = ref<HTMLDivElement>()
const cornerDragger = ref<HTMLDivElement>()
const previewWrapper = ref<HTMLDivElement>()
useDragger(horizontalDragger, responsiveWidth, 10, 20000, 'x')
useDragger(verticalDragger, responsiveHeight, 10, 20000, 'y')
useDragger(cornerDragger, responsiveWidth, 10, 20000, 'x')
useDragger(cornerDragger, responsiveHeight, 10, 20000, 'y')
useDragger(horizontalDragger, responsiveWidth, 32, 20000, 'x')
useDragger(verticalDragger, responsiveHeight, 32, 20000, 'y')
useDragger(cornerDragger, responsiveWidth, 32, 20000, 'x')
useDragger(cornerDragger, responsiveHeight, 32, 20000, 'y')
// Handle rotate
const finalWidth = computed(() => props.settings.rotate ? props.settings.responsiveHeight : props.settings.responsiveWidth)
const finalHeight = computed(() => props.settings.rotate ? props.settings.responsiveWidth : props.settings.responsiveHeight)
const finalWidth = computed(() => settings.rotate ? settings.responsiveHeight : settings.responsiveWidth)
const finalHeight = computed(() => settings.rotate ? settings.responsiveWidth : settings.responsiveHeight)
// Disabled responsive
Expand All @@ -235,12 +208,15 @@ const isResponsiveEnabled = computed(() => !props.variant.responsiveDisabled)
/>
</div>

<div class="htw-h-full htw-overflow-auto htw-relative">
<div
ref="previewWrapper"
class="htw-h-full htw-overflow-auto htw-relative"
>
<div
class="htw-h-full htw-p-4 htw-overflow-hidden htw-bg-white dark:htw-bg-gray-700 htw-rounded-lg htw-relative"
:style="isResponsiveEnabled ? {
width: finalWidth ? `${finalWidth + 44}px` : null,
height: finalHeight ? `${finalHeight + 44}px` : null,
:class="isResponsiveEnabled ? {
'htw-w-fit': !!finalWidth,
'htw-h-fit': !!finalHeight
} : undefined"
>
<div class="htw-p-4 htw-h-full htw-relative">
Expand All @@ -260,6 +236,10 @@ const isResponsiveEnabled = computed(() => !props.variant.responsiveDisabled)
'htw-invisible': !isIframeLoaded,
'htw-pointer-events-none': resizing,
}"
:style="isResponsiveEnabled ? {
width: finalWidth ? `${finalWidth}px` : null,
height: finalHeight ? `${finalHeight}px` : null,
} : undefined"
data-test-id="preview-iframe"
@load="onIframeLoad()"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<script lang="ts" setup>
import { PropType } from 'vue'
import type { Story, Variant } from '../../types'
import { usePreviewSettings } from '../../util/preview-settings'
import StoryResponsivePreview from './StoryResponsivePreview.vue'
import { isMobile } from '../../util/responsive'
import StoryVariantTitle from './variant/StoryVariantTitle.vue'
Expand All @@ -13,8 +11,6 @@ defineProps<{
variant: Variant
story: Story
}>()
const settings = usePreviewSettings()
</script>

<template>
Expand Down Expand Up @@ -42,7 +38,6 @@ const settings = usePreviewSettings()

<!-- Preview -->
<StoryResponsivePreview
v-model:settings="settings"
:story="story"
:variant="variant"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts" setup>
import { Icon } from '@iconify/vue'
import { usePreviewSettingsStore } from '../../../stores/preview-settings'
import { histoireConfig } from '../../../util/config'
import { usePreviewSettings } from '../../../util/preview-settings'
import BaseCheckbox from '../../base/BaseCheckbox.vue'
const settings = usePreviewSettings()
const settings = usePreviewSettingsStore().currentSettings
</script>

<template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts" setup>
import { Icon } from '@iconify/vue'
import { usePreviewSettingsStore } from '../../../stores/preview-settings'
import { histoireConfig } from '../../../util/config'
import { usePreviewSettings } from '../../../util/preview-settings'
import BaseCheckbox from '../../base/BaseCheckbox.vue'
const settings = usePreviewSettings()
const settings = usePreviewSettingsStore().currentSettings
</script>

<template>
Expand Down
17 changes: 17 additions & 0 deletions packages/histoire/src/client/app/stores/preview-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import type { PreviewSettings } from '../types'

export const usePreviewSettingsStore = defineStore('preview-settings', () => {
const currentSettings = useStorage<PreviewSettings>('_histoire-sandbox-settings-v2', {
responsiveWidth: 720,
responsiveHeight: null,
rotate: false,
backgroundColor: 'transparent',
checkerboard: false,
})

return {
currentSettings,
}
})
8 changes: 8 additions & 0 deletions packages/histoire/src/client/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,11 @@ export interface SearchResult {
icon?: string
iconColor?: string
}

export interface PreviewSettings {
responsiveWidth: number
responsiveHeight: number
rotate: boolean
backgroundColor: string
checkerboard: boolean
}
22 changes: 1 addition & 21 deletions packages/histoire/src/client/app/util/preview-settings.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
import { useStorage } from '@vueuse/core'
import { reactive } from 'vue'

export interface PreviewSettings {
responsiveWidth: number
responsiveHeight: number
rotate: boolean
backgroundColor: string
checkerboard: boolean
}

const previewSettings = useStorage<PreviewSettings>('_histoire-sandbox-settings-v2', {
responsiveWidth: 720,
responsiveHeight: null,
rotate: false,
backgroundColor: 'transparent',
checkerboard: false,
})

export function usePreviewSettings () {
return previewSettings
}
import type { PreviewSettings } from '../types'

export const receivedSettings = reactive<PreviewSettings>({} as PreviewSettings)

Expand Down

0 comments on commit c53cc5c

Please sign in to comment.