feat: add gold day calculator and polish desktop UI
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const DRAG_THRESHOLD = 6
|
||||
const POSITION_VERSION = 1
|
||||
/** v2: دسکتاپ از کنار سایدبار تا انتهای viewport (نه ستون ۴۲۶px) */
|
||||
const POSITION_VERSION = 2
|
||||
const MOBILE_CONTENT_MAX = 426
|
||||
|
||||
type FabPosition = { x: number; y: number }
|
||||
|
||||
@@ -11,6 +13,11 @@ type SavedFabOffset = {
|
||||
dy: number
|
||||
}
|
||||
|
||||
type HorizontalBounds = {
|
||||
offsetX: number
|
||||
contentWidth: number
|
||||
}
|
||||
|
||||
type DraggableFloatingOptions = {
|
||||
width?: number
|
||||
height?: number
|
||||
@@ -40,6 +47,46 @@ function readBottomChromeHeight(): number {
|
||||
return measured > 0 ? measured : nav
|
||||
}
|
||||
|
||||
function isDesktopLayout(): boolean {
|
||||
const el = document.documentElement
|
||||
return (
|
||||
el.classList.contains('app-layout--desktop')
|
||||
|| el.getAttribute('data-layout') === 'desktop'
|
||||
)
|
||||
}
|
||||
|
||||
function readCssLengthPx(varName: string): number | null {
|
||||
const raw = getComputedStyle(document.documentElement).getPropertyValue(varName).trim()
|
||||
if (!raw) return null
|
||||
|
||||
if (raw.endsWith('px')) {
|
||||
const n = parseFloat(raw)
|
||||
return Number.isNaN(n) ? null : n
|
||||
}
|
||||
|
||||
if (raw.endsWith('rem')) {
|
||||
const rem = parseFloat(raw)
|
||||
const root = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16
|
||||
return Number.isNaN(rem) ? null : rem * root
|
||||
}
|
||||
|
||||
const n = parseFloat(raw)
|
||||
return Number.isNaN(n) ? null : n
|
||||
}
|
||||
|
||||
/** سایدبار دسکتاپ معمولاً سمت راست (RTL) است. */
|
||||
function readDesktopSidebarWidthPx(): number {
|
||||
if (document.documentElement.classList.contains('app-layout--desktop-no-sidebar')) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return (
|
||||
readCssLengthPx('--app-desktop-sidebar-width')
|
||||
?? readCssLengthPx('--app-sidebar-width')
|
||||
?? 260
|
||||
)
|
||||
}
|
||||
|
||||
export function useDraggableFloatingButton(
|
||||
storageKey: string,
|
||||
options: DraggableFloatingOptions = {}
|
||||
@@ -66,22 +113,28 @@ export function useDraggableFloatingButton(
|
||||
touchAction: 'none'
|
||||
}))
|
||||
|
||||
function getViewportWidth() {
|
||||
return Math.min(window.innerWidth, 426)
|
||||
}
|
||||
|
||||
function getHorizontalOffset() {
|
||||
function getHorizontalBounds(): HorizontalBounds {
|
||||
const vw = window.innerWidth
|
||||
const contentWidth = getViewportWidth()
|
||||
|
||||
return Math.max(0, (vw - contentWidth) / 2)
|
||||
if (isDesktopLayout()) {
|
||||
const sidebar = readDesktopSidebarWidthPx()
|
||||
return {
|
||||
offsetX: 0,
|
||||
contentWidth: Math.max(width, vw - sidebar)
|
||||
}
|
||||
}
|
||||
|
||||
const contentWidth = Math.min(vw, MOBILE_CONTENT_MAX)
|
||||
return {
|
||||
offsetX: Math.max(0, (vw - contentWidth) / 2),
|
||||
contentWidth
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultPosition(): FabPosition {
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
const bottomChrome = readBottomChromeHeight()
|
||||
const { offsetX, contentWidth } = getHorizontalBounds()
|
||||
const bottomChrome = isDesktopLayout() ? 0 : readBottomChromeHeight()
|
||||
|
||||
return {
|
||||
x: offsetX + contentWidth - width - defaultRightOffset,
|
||||
@@ -100,8 +153,7 @@ export function useDraggableFloatingButton(
|
||||
|
||||
function clampToViewport() {
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
const { offsetX, contentWidth } = getHorizontalBounds()
|
||||
const minX = offsetX
|
||||
const maxX = offsetX + contentWidth - width
|
||||
|
||||
|
||||
Reference in New Issue
Block a user