Skip to content

Commit

Permalink
feat: new responsive view with height
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Mar 10, 2022
1 parent 5dcf343 commit 5f49d67
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 238 deletions.
16 changes: 13 additions & 3 deletions examples/vue3/src/components/Responsive.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<template>
<div class="target">
<p>
<p
v-for="n in 100"
:key="n"
>
Responsive
</p>
</div>
</template>

<style scoped>
.target p {
margin: 2px;
padding: 12px;
background-color: rgb(188, 130, 255);
color: black;
}
@media (max-width: 768px) {
.target {
background-color: rgb(236, 132, 132);
.target p {
background-color: rgb(115, 255, 173);
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<script lang="ts" setup>
import { computed, Ref, ref, toRaw, toRefs, watch } from 'vue'
import { useEventListener } from '@vueuse/core'
import { Icon } from '@iconify/vue'
import { STATE_SYNC, PREVIEW_SETTINGS_SYNC } 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'
const props = defineProps<{
story: Story
variant: Variant
settings: PreviewSettings
}>()
// Iframe
const iframe = ref<HTMLIFrameElement>()
function syncState () {
if (iframe.value) {
iframe.value.contentWindow.postMessage({
type: STATE_SYNC,
state: toRaw(props.variant.state),
})
}
}
let synced = false
watch(() => props.variant.state, () => {
if (synced) {
synced = false
return
}
syncState()
}, {
deep: true,
immediate: true,
})
useEventListener(window, 'message', (event) => {
if (event.data.type === STATE_SYNC) {
synced = true
Object.assign(props.variant.state, event.data.state)
}
})
const sandboxUrl = computed(() => {
const url = new URLSearchParams()
url.append('storyId', props.story.id)
url.append('variantId', props.variant.id)
return '/__sandbox?' + url.toString()
})
const isIframeLoaded = ref(false)
watch(sandboxUrl, () => {
isIframeLoaded.value = false
})
// Settings
function syncSettings () {
if (iframe.value) {
iframe.value.contentWindow.postMessage({
type: PREVIEW_SETTINGS_SYNC,
settings: toRaw(props.settings),
})
}
}
watch(() => props.settings, value => {
syncSettings()
}, {
deep: true,
immediate: true,
})
// Iframe load
function onIframeLoad () {
isIframeLoaded.value = true
syncState()
syncSettings()
}
// Resize
const resizing = ref(false)
function useDragger (el: Ref<HTMLDivElement>, value: Ref<number>, min: number, max: number, axis: 'x' | 'y') {
const onMouseDown = (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
const start = axis === 'x' ? event.clientX : event.clientY
const startValue = value.value || (axis === 'x' ? iframe.value.offsetWidth : iframe.value.offsetHeight)
resizing.value = true
const onMouseMove = (event: MouseEvent) => {
const delta = (axis === 'x' ? event.clientX : event.clientY) - start
value.value = Math.max(min, Math.min(max, startValue + delta))
}
const onMouseUp = () => {
window.removeEventListener('mousemove', onMouseMove)
window.removeEventListener('mouseup', onMouseUp)
resizing.value = false
}
window.addEventListener('mousemove', onMouseMove)
window.addEventListener('mouseup', onMouseUp)
}
useEventListener(el, 'mousedown', onMouseDown)
}
const { responsiveWidth, responsiveHeight } = toRefs(props.settings)
const horizontalDragger = ref<HTMLDivElement>()
const verticalDragger = ref<HTMLDivElement>()
const cornerDragger = 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')
// 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)
</script>

<template>
<div class="htw-w-full htw-h-full htw-flex-1 htw-border htw-border-gray-100 dark:htw-border-gray-800 htw-rounded-lg htw-relative htw-overflow-hidden">
<div class="htw-absolute htw-inset-0 htw-w-full htw-h-full htw-bg-gray-200 dark:htw-bg-gray-850 htw-rounded-r-lg htw-border-l-2 htw-border-gray-500/10 dark:htw-border-gray-700/30 htw-overflow-hidden">
<HatchedPattern
class="htw-w-full htw-h-full htw-text-black/[2%] dark:htw-text-white/[2%]"
/>
</div>

<div 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="{
width: finalWidth ? `${finalWidth + 44}px` : null,
height: finalHeight ? `${finalHeight + 44}px` : null,
}"
>
<div class="htw-p-4 htw-h-full htw-relative">
<div class="htw-w-full htw-h-full htw-border htw-border-gray-100 dark:htw-border-gray-800 htw-rounded-sm htw-relative">
<div class="bind-preview-bg htw-absolute htw-inset-0" />

<CheckerboardPattern
v-if="settings.checkerboard"
class="htw-absolute htw-inset-0 htw-w-full htw-h-full htw-text-gray-500/20"
/>

<iframe
ref="iframe"
:src="sandboxUrl"
class="htw-w-full htw-h-full htw-relative"
:class="{
'htw-invisible': !isIframeLoaded,
'htw-pointer-events-none': resizing,
}"
@load="onIframeLoad()"
/>
</div>

<!-- Markers -->
<div class="htw-absolute htw-top-1 htw-left-4 htw-h-2 htw-w-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-top-1 htw-right-4 htw-h-2 htw-w-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-bottom-1 htw-left-4 htw-h-2 htw-w-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-bottom-1 htw-right-4 htw-h-2 htw-w-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-left-1 htw-top-4 htw-w-2 htw-h-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-left-1 htw-bottom-4 htw-w-2 htw-h-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-right-1 htw-top-4 htw-w-2 htw-h-px htw-bg-gray-200 dark:htw-bg-gray-800" />
<div class="htw-absolute htw-right-1 htw-bottom-4 htw-w-2 htw-h-px htw-bg-gray-200 dark:htw-bg-gray-800" />
</div>

<!-- Resize Dragger -->
<div
ref="horizontalDragger"
class="htw-absolute htw-w-4 htw-top-0 htw-bottom-4 htw-right-0 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-items-center htw-justify-center htw-cursor-ew-resize htw-group hover:htw-text-primary-500"
>
<Icon
icon="mdi:drag-vertical-variant"
class="htw-w-4 htw-h-4 htw-opacity-20 group-hover:htw-opacity-90"
/>
</div>
<div
ref="verticalDragger"
class="htw-absolute htw-h-4 htw-left-0 htw-right-4 htw-bottom-0 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-items-center htw-justify-center htw-cursor-ns-resize htw-group hover:htw-text-primary-500"
>
<Icon
icon="mdi:drag-horizontal-variant"
class="htw-w-4 htw-h-4 htw-opacity-20 group-hover:htw-opacity-90"
/>
</div>
<div
ref="cornerDragger"
class="htw-absolute htw-w-4 htw-h-4 htw-right-0 htw-bottom-0 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-items-center htw-justify-center htw-cursor-nwse-resize htw-group hover:htw-text-primary-500"
/>
</div>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { PropType, toRefs } from 'vue'
import { useCurrentVariantRoute } from '../../composable/variant'
import { useCurrentVariantRoute } from '../../util/variant'
import type { Story, Variant } from '../../types'
import SandboxVue3 from '../sandbox/SandboxVue3.vue'
import { Icon } from '@iconify/vue'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { PropType, toRefs } from 'vue'
import { useCurrentVariantRoute } from '../../composable/variant'
import { useCurrentVariantRoute } from '../../util/variant'
import type { Variant } from '../../types'
import BaseListItemLink from '../base/BaseListItemLink.vue'
import { Icon } from '@iconify/vue'
Expand Down
Loading

0 comments on commit 5f49d67

Please sign in to comment.