UNPKG

@dialpad/dialtone

Version:

Dialpad's Dialtone design system monorepo

1 lines 30.6 kB
{"version":3,"file":"resizable_handle-RIKS8frB.cjs","names":[],"sources":["../components/resizable/composables/useResizableKeyboard.ts","../components/resizable/resizable_handle.vue"],"sourcesContent":["import { ref, type ComputedRef, type Ref } from 'vue';\nimport type {\n ResizablePanelState,\n ResizableDirection,\n} from '../resizable_constants';\nimport { MIN_PANEL_SIZE_PX, buildHandleId } from '../resizable_constants';\nimport { useResizeHandling } from './useResizableCalculations';\n\nexport interface ResizableKeyboardMessages {\n /**\n * Announcement template for resize actions.\n * Placeholders: {beforeId}, {afterId}, {beforePx}, {afterPx}\n */\n resizeAnnouncement?: string;\n /** Announced when a panel is collapsed. Placeholder: {panelId} */\n collapseAnnouncement?: string;\n /** Announced when a panel is expanded. Placeholder: {panelId} */\n expandAnnouncement?: string;\n /** Announced when panels are reset. Placeholders: {beforeId}, {afterId} */\n resetAnnouncement?: string;\n /** aria-valuetext template. Placeholders: {panelId}, {pixels} */\n ariaValueText?: string;\n /** aria-label template. Placeholders: {before}, {after} */\n handleAriaLabel?: string;\n}\n\nconst DEFAULT_KEYBOARD_MESSAGES: Required<ResizableKeyboardMessages> = {\n resizeAnnouncement:\n '{beforeId}: {beforePx}px, {afterId}: {afterPx}px',\n collapseAnnouncement: '{panelId} collapsed',\n expandAnnouncement: '{panelId} expanded',\n resetAnnouncement: '{beforeId} and {afterId} reset',\n ariaValueText: '{panelId}: {pixels}px',\n handleAriaLabel: 'Resize handle between {before} and {after} panels',\n};\n\nexport interface ResizableKeyboardOptions {\n panels: ComputedRef<ResizablePanelState[]>;\n direction: ComputedRef<ResizableDirection>;\n containerSize: ComputedRef<number>;\n beforePanelId: ComputedRef<string>;\n afterPanelId: ComputedRef<string>;\n handleElement: Ref<HTMLElement | null>;\n onResize: (\n beforePanelId: string,\n beforeSize: number,\n afterPanelId: string,\n afterSize: number,\n ) => void;\n onCollapse?: (panelId: string, collapsed: boolean) => void;\n onReset?: (beforePanelId: string, afterPanelId: string) => void;\n onSizeAnnouncement?: (message: string) => void;\n messages?: ResizableKeyboardMessages;\n}\n\n/**\n * Keyboard increment settings for different modifier combinations (pixels).\n */\nexport const KEYBOARD_INCREMENTS = {\n /** Fine control (Cmd/Ctrl + Arrow) */\n fine: 1,\n /** Normal increment (Arrow only) */\n normal: 8,\n /** Large increment (Shift + Arrow) */\n large: 24,\n} as const;\n\n/**\n * Composable for keyboard-driven resize on a single handle.\n * Implements the W3C ARIA separator keyboard pattern:\n * - Arrow keys: resize\n * - Enter: toggle collapse on before panel\n * - Home: resize to min\n * - End: resize to max\n * - R: reset adjacent panels\n * - Escape: blur handle\n */\nexport function useResizableKeyboard(options: ResizableKeyboardOptions) {\n const {\n panels,\n direction,\n containerSize,\n beforePanelId,\n afterPanelId,\n handleElement,\n onResize,\n onCollapse,\n onReset,\n onSizeAnnouncement,\n messages: userMessages,\n } = options;\n\n const msg = { ...DEFAULT_KEYBOARD_MESSAGES, ...userMessages };\n\n const isFocused = ref(false);\n\n const resizeHandler = useResizeHandling(() => containerSize.value);\n\n function getCurrentPanels() {\n const beforePanel = panels.value.find(\n (p) => p.id === beforePanelId.value,\n );\n const afterPanel = panels.value.find(\n (p) => p.id === afterPanelId.value,\n );\n return { beforePanel, afterPanel };\n }\n\n function getResizeIncrement(event: KeyboardEvent): number {\n if (event.metaKey || event.ctrlKey) return KEYBOARD_INCREMENTS.fine;\n if (event.shiftKey) return KEYBOARD_INCREMENTS.large;\n return KEYBOARD_INCREMENTS.normal;\n }\n\n function getResizeDirection(\n key: string,\n layoutDirection: ResizableDirection,\n ): 'increase' | 'decrease' | null {\n const rowKeyMap: Record<string, 'increase' | 'decrease'> = {\n ArrowLeft: 'decrease',\n ArrowRight: 'increase',\n };\n const columnKeyMap: Record<string, 'increase' | 'decrease'> = {\n ArrowUp: 'decrease',\n ArrowDown: 'increase',\n };\n return layoutDirection === 'row'\n ? rowKeyMap[key] || null\n : columnKeyMap[key] || null;\n }\n\n function generateSizeAnnouncement(\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState,\n ): string {\n const beforePx = Math.round(beforePanel.pixelSize);\n const afterPx = Math.round(afterPanel.pixelSize);\n\n return msg.resizeAnnouncement\n .replace('{beforeId}', beforePanel.id)\n .replace('{afterId}', afterPanel.id)\n .replace('{beforePx}', String(beforePx))\n .replace('{afterPx}', String(afterPx));\n }\n\n // ─── DOM position updates (logical properties) ──────────────────────\n\n function updateLayout(\n beforeEl: HTMLElement,\n afterEl: HTMLElement,\n handleEl: HTMLElement | null,\n cursorPos: number,\n beforeEnd: number,\n ): void {\n beforeEl.style.insetInlineStart = beforeEl.style.insetInlineStart || '0px';\n beforeEl.style.insetInlineEnd = `${beforeEnd}px`;\n beforeEl.style.inlineSize = '';\n\n afterEl.style.insetInlineStart = `${cursorPos}px`;\n afterEl.style.inlineSize = '';\n\n if (handleEl) {\n handleEl.style.insetInlineStart = `${Math.max(0, cursorPos - 2)}px`;\n }\n }\n\n function clearInlineStyles(\n beforeEl: HTMLElement,\n afterEl: HTMLElement,\n handleEl: HTMLElement | null,\n ): void {\n beforeEl.style.insetInlineStart = '';\n beforeEl.style.insetInlineEnd = '';\n beforeEl.style.inlineSize = '';\n afterEl.style.insetInlineStart = '';\n afterEl.style.inlineSize = '';\n if (handleEl) handleEl.style.insetInlineStart = '';\n }\n\n function applyResize(\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState,\n newBeforePixels: number,\n newAfterPixels: number,\n ): void {\n const roundedBefore = Math.round(newBeforePixels);\n const roundedAfter = Math.round(newAfterPixels);\n\n // DOM updates for immediate visual feedback\n const cursorPos = newBeforePixels;\n const container = handleElement.value?.closest('.d-resizable');\n\n let beforeEl: HTMLElement | null = null;\n let afterEl: HTMLElement | null = null;\n let hEl: HTMLElement | null = null;\n\n if (container) {\n beforeEl = container.querySelector(\n `[data-panel-id=\"${beforePanelId.value}\"]`,\n );\n afterEl = container.querySelector(\n `[data-panel-id=\"${afterPanelId.value}\"]`,\n );\n hEl = container.querySelector(\n `[data-handle-id=\"${buildHandleId(beforePanelId.value, afterPanelId.value)}\"]`,\n );\n\n if (beforeEl && afterEl) {\n const beforeEnd = containerSize.value - cursorPos;\n updateLayout(beforeEl, afterEl, hEl, cursorPos, beforeEnd);\n }\n }\n\n // Commit to reactive state — mirrors drag's commitDrag discipline\n onResize(\n beforePanelId.value,\n roundedBefore,\n afterPanelId.value,\n roundedAfter,\n );\n\n // Clear inline styles so Vue exclusively owns the DOM from this point.\n // Without this, there's a frame where both inline styles and Vue's\n // computed layout coexist — a latent bug surface if anything reads\n // DOM positions between onResize and Vue's next render.\n if (beforeEl && afterEl) {\n clearInlineStyles(beforeEl, afterEl, hEl);\n }\n }\n\n function processKeyboardResize(\n beforePanel: ResizablePanelState,\n afterPanel: ResizablePanelState,\n resizeDirection: 'increase' | 'decrease',\n incrementPixels: number,\n ): boolean {\n const delta =\n resizeDirection === 'increase' ? incrementPixels : -incrementPixels;\n const newCursorPosition = beforePanel.pixelSize + delta;\n\n const resizeResult = resizeHandler.processResizeMove(\n newCursorPosition,\n beforePanel,\n afterPanel,\n containerSize.value,\n buildHandleId(beforePanelId.value, afterPanelId.value),\n panels.value,\n 0,\n );\n\n if (!resizeResult.isValidResize) return false;\n\n applyResize(\n beforePanel,\n afterPanel,\n resizeResult.beforePanelSize,\n resizeResult.afterPanelSize,\n );\n\n if (onSizeAnnouncement) {\n onSizeAnnouncement(\n generateSizeAnnouncement(beforePanel, afterPanel),\n );\n }\n\n return true;\n }\n\n // ─── Key handlers ─────────────────────────────────────────────────\n\n function handleArrowKey(event: KeyboardEvent): void {\n const resizeDirection = getResizeDirection(event.key, direction.value);\n if (!resizeDirection) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n const { beforePanel, afterPanel } = getCurrentPanels();\n if (!beforePanel || !afterPanel) return;\n\n const incrementPixels = getResizeIncrement(event);\n processKeyboardResize(\n beforePanel, afterPanel, resizeDirection, incrementPixels,\n );\n }\n\n function handleEnterKey(event: KeyboardEvent): void {\n const { beforePanel } = getCurrentPanels();\n if (!beforePanel?.collapsible || !onCollapse) return;\n\n event.preventDefault();\n const newCollapsed = !beforePanel.collapsed;\n onCollapse(beforePanel.id, newCollapsed);\n\n if (onSizeAnnouncement) {\n const template = newCollapsed\n ? msg.collapseAnnouncement\n : msg.expandAnnouncement;\n onSizeAnnouncement(template.replace('{panelId}', beforePanel.id));\n }\n }\n\n function handleHomeKey(event: KeyboardEvent): void {\n event.preventDefault();\n\n const { beforePanel, afterPanel } = getCurrentPanels();\n if (!beforePanel || !afterPanel) return;\n\n const targetSize = beforePanel.userMinSizePixels ?? MIN_PANEL_SIZE_PX;\n const delta = targetSize - beforePanel.pixelSize;\n if (delta === 0) return;\n\n const dir = delta > 0 ? 'increase' : 'decrease';\n processKeyboardResize(beforePanel, afterPanel, dir, Math.abs(delta));\n }\n\n function handleEndKey(event: KeyboardEvent): void {\n event.preventDefault();\n\n const { beforePanel, afterPanel } = getCurrentPanels();\n if (!beforePanel || !afterPanel) return;\n\n const afterMin = afterPanel.userMinSizePixels ?? MIN_PANEL_SIZE_PX;\n const maxSize = beforePanel.userMaxSizePixels\n ?? (containerSize.value - afterMin);\n const targetSize = Math.min(maxSize, containerSize.value - afterMin);\n const delta = targetSize - beforePanel.pixelSize;\n if (delta === 0) return;\n\n const dir = delta > 0 ? 'increase' : 'decrease';\n processKeyboardResize(beforePanel, afterPanel, dir, Math.abs(delta));\n }\n\n function handleResetKey(event: KeyboardEvent): void {\n if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return;\n\n event.preventDefault();\n\n const { beforePanel, afterPanel } = getCurrentPanels();\n if (!beforePanel || !afterPanel || !onReset) return;\n\n onReset(beforePanel.id, afterPanel.id);\n\n if (onSizeAnnouncement) {\n onSizeAnnouncement(\n msg.resetAnnouncement\n .replace('{beforeId}', beforePanel.id)\n .replace('{afterId}', afterPanel.id),\n );\n }\n }\n\n function handleEscapeKey(event: KeyboardEvent): void {\n event.preventDefault();\n handleElement.value?.blur();\n }\n\n // ─── Event handlers ─────────────────────────────────────────────────\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (!isFocused.value) return;\n\n switch (event.key) {\n case 'Enter':\n handleEnterKey(event);\n return;\n case 'Home':\n handleHomeKey(event);\n return;\n case 'End':\n handleEndKey(event);\n return;\n case 'r':\n case 'R':\n handleResetKey(event);\n return;\n case 'Escape':\n handleEscapeKey(event);\n return;\n }\n\n // Arrow keys — resize\n handleArrowKey(event);\n }\n\n function handleFocus(): void {\n isFocused.value = true;\n }\n\n function handleBlur(): void {\n isFocused.value = false;\n }\n\n function focusHandle(): void {\n handleElement.value?.focus();\n }\n\n return {\n isFocused,\n focusHandle,\n handleKeyDown,\n handleFocus,\n handleBlur,\n KEYBOARD_INCREMENTS,\n };\n}\n","<template>\n <!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions -->\n <div\n ref=\"handleElement\"\n :class=\"[\n 'd-resizable-handle',\n `d-resizable-handle--${direction}`,\n props.class,\n {\n 'd-resizable-handle--active': isActive,\n 'd-resizable-handle--disabled': isDisabled,\n },\n ]\"\n :style=\"handleStyles\"\n :data-handle-id=\"handleId\"\n :tabindex=\"isDisabled ? '-1' : '0'\"\n role=\"separator\"\n :aria-orientation=\"direction === 'row' ? 'vertical' : 'horizontal'\"\n :aria-label=\"computedAriaLabel\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuemin=\"ariaValueMin\"\n :aria-valuemax=\"ariaValueMax\"\n :aria-controls=\"`dt-resizable-panel-${resolvedBeforePanelId}`\"\n :aria-valuetext=\"ariaValueText\"\n :aria-disabled=\"isDisabled || undefined\"\n @pointerdown=\"handlePointerDown\"\n @dblclick=\"handleDoubleClick\"\n @keydown=\"handleKeyDown\"\n @focus=\"handleFocusEvent\"\n @blur=\"handleBlurEvent\"\n >\n <div class=\"d-resizable-handle__indicator\" />\n </div>\n</template>\n\n<script setup>\nimport { computed, inject, onMounted, onUnmounted, ref, getCurrentInstance } from 'vue';\nimport { RESIZABLE_CONTEXT_KEY, buildHandleId } from './resizable_constants';\nimport { pixelsToPercentage } from './resizable_utils';\nimport { useResizableKeyboard } from './composables/useResizableKeyboard';\n\nconst props = defineProps({\n /** ID of the panel before this handle. Auto-detected from layout order if not set. */\n beforePanelId: { type: String, default: null },\n /** ID of the panel after this handle. Auto-detected from layout order if not set. */\n afterPanelId: { type: String, default: null },\n /** Disable resize interaction for this handle. */\n disabled: { type: Boolean, default: false },\n /** Additional CSS classes applied to the handle element. */\n class: { type: [String, Object, Array], default: '' },\n /** Disable the double-click reset behavior. */\n disableResetOnDoubleClick: { type: Boolean, default: false },\n /**\n * Which panels to reset on double-click.\n * @values 'both', 'before', 'after', 'all'\n */\n resetBehavior: { type: String, default: 'both' },\n /** Override the default aria-label for i18n. */\n ariaLabel: { type: String, default: null },\n});\n\n// ── Injected context from DtResizable ────────────────────────────────────\n\nconst ctx = inject(RESIZABLE_CONTEXT_KEY, null);\nconst layoutRef = ctx?.layout ?? computed(() => ({ panels: new Map(), handles: [] }));\nconst panels = ctx?.panels ?? computed(() => []);\nconst directionRef = ctx?.direction ?? computed(() => 'row');\nconst containerSizeRef = ctx?.containerSize ?? computed(() => 1000);\nconst activeHandleId = ctx?.activeHandleId ?? computed(() => undefined);\nconst startResize = ctx?.startResize ?? (() => {});\nconst resetPanels = ctx?.resetPanels ?? (() => {});\nconst registerHandle = ctx?.registerHandle ?? (() => 0);\nconst unregisterHandle = ctx?.unregisterHandle ?? (() => {});\nconst saveToStorage = ctx?.saveToStorage ?? null;\nconst collapsePanel = ctx?.collapsePanel ?? null;\nconst commitPanelSize = ctx?.commitPanelSize ?? null;\nconst announce = ctx?.announce ?? null;\nconst panelMap = ctx?.panelMap ?? computed(() => new Map());\nconst injectedMessages = ctx?.messages ?? {};\n\n// ── Handle registration ──────────────────────────────────────────────────────\n\nconst currentInstance = getCurrentInstance();\n\n// ── Layout-driven position ───────────────────────────────────────────────────\n\n// DOM index resolved once at mount time to avoid repeated querySelectorAll calls\nconst resolvedDomIndex = ref(0);\n\nconst handlePosition = computed(() => {\n const layout = layoutRef.value;\n if (layout.handles.length === 0) return null;\n\n if (props.beforePanelId && props.afterPanelId) {\n const id = buildHandleId(props.beforePanelId, props.afterPanelId);\n return layout.handles.find(h => h.id === id) ?? null;\n }\n\n return layout.handles[resolvedDomIndex.value] ?? null;\n});\n\n/** The composite handle identifier used by the drag system */\nconst handleId = computed(() => handlePosition.value?.id ?? '');\n\n/** Panel ID before this handle (from layout result) */\nconst resolvedBeforePanelId = computed(() => handlePosition.value?.beforePanelId ?? props.beforePanelId ?? '');\n\n/** Panel ID after this handle (from layout result) */\nconst resolvedAfterPanelId = computed(() => handlePosition.value?.afterPanelId ?? props.afterPanelId ?? '');\n\n/** Whether this handle is currently the active drag handle */\nconst isActive = computed(() => !!(handleId.value && activeHandleId.value === handleId.value));\n\n/**\n * Handle is disabled when:\n * - props.disabled is set, OR\n * - the layout marks this handle as disabled (adjacent collapsed/non-resizable panel)\n */\nconst isDisabled = computed(() => props.disabled || (handlePosition.value?.disabled ?? false));\n\n/** Reactive layout direction */\nconst direction = directionRef;\n\n// ── Position styles from layout ──────────────────────────────────────────────\n\nconst handleStyles = computed(() => {\n const pos = handlePosition.value;\n\n if (!pos) {\n return { visibility: 'hidden' };\n }\n\n return {\n insetInlineStart: `${Math.max(0, pos.left)}px`,\n visibility: '',\n ...offsetHandleStyles.value,\n };\n});\n\n// ── ARIA values ──────────────────────────────────────────────────────────────\n\nconst computedAriaLabel = computed(() => {\n if (props.ariaLabel) return props.ariaLabel;\n const before = resolvedBeforePanelId.value || 'first';\n const after = resolvedAfterPanelId.value || 'second';\n const template = injectedMessages.handleAriaLabel\n ?? 'Resize handle between {before} and {after} panels';\n return template.replace('{before}', before).replace('{after}', after);\n});\n\nconst ariaValueText = computed(() => {\n const p = panelMap.value.get(resolvedBeforePanelId.value);\n if (!p) return '';\n const template = injectedMessages.ariaValueText ?? '{panelId}: {pixels}px';\n return template\n .replace('{panelId}', resolvedBeforePanelId.value)\n .replace('{pixels}', String(Math.round(p.pixelSize)));\n});\n\nconst ariaValueNow = computed(() => {\n const p = panelMap.value.get(resolvedBeforePanelId.value);\n const cs = containerSizeRef.value;\n if (!p || !cs) return 0;\n return Math.floor(pixelsToPercentage(p.collapsed ? 0 : p.pixelSize || 0, cs));\n});\n\nconst ariaValueMin = computed(() => {\n const p = panelMap.value.get(resolvedBeforePanelId.value);\n const cs = containerSizeRef.value;\n if (!p || !cs) return 0;\n return Math.floor(pixelsToPercentage(p.userMinSizePixels || 0, cs));\n});\n\nconst ariaValueMax = computed(() => {\n const p = panelMap.value.get(resolvedBeforePanelId.value);\n const cs = containerSizeRef.value;\n if (!p || !cs) return 100;\n return Math.floor(pixelsToPercentage(p.userMaxSizePixels || cs, cs));\n});\n\n// ── Handle DOM element ───────────────────────────────────────────────────────\n\nconst handleElement = ref(null);\n\n// ── Keyboard resize composable ─────────────────────────────────────────────\n\nconst keyboard = useResizableKeyboard({\n panels,\n direction: directionRef,\n containerSize: containerSizeRef,\n beforePanelId: resolvedBeforePanelId,\n afterPanelId: resolvedAfterPanelId,\n handleElement,\n onResize (beforeId, beforeSize, afterId, afterSize) {\n if (commitPanelSize) {\n commitPanelSize(beforeId, beforeSize);\n commitPanelSize(afterId, afterSize);\n } else {\n saveToStorage?.();\n }\n },\n onCollapse (panelId, collapsed) { collapsePanel?.(panelId, collapsed); },\n onReset (beforeId, afterId) { resetPanels?.(beforeId, afterId, 'both'); },\n onSizeAnnouncement (msg) { announce?.(msg); },\n messages: injectedMessages,\n});\n\n// ── Offset (from parent context) ──────────────────────────────────────────\nconst offsetHandleStyles = ctx?.offsetHandleStyles ?? computed(() => ({}));\n\n// ── Lifecycle ────────────────────────────────────────────────────────────────\n\nonMounted(() => {\n registerHandle(currentInstance);\n\n const el = handleElement.value;\n if (el?.parentElement) {\n const allHandles = Array.from(el.parentElement.querySelectorAll('.d-resizable-handle'));\n const idx = allHandles.indexOf(el);\n if (idx >= 0) resolvedDomIndex.value = idx;\n }\n});\n\nonUnmounted(() => {\n unregisterHandle(currentInstance);\n});\n\n// ── Event handlers ───────────────────────────────────────────────────────────\n\nfunction handlePointerDown (event) {\n if (isDisabled.value) return;\n event.preventDefault();\n startResize(handleId.value);\n}\n\nfunction handleDoubleClick () {\n if (props.disableResetOnDoubleClick) return;\n if (isDisabled.value) return;\n\n const beforeId = resolvedBeforePanelId.value;\n const afterId = resolvedAfterPanelId.value;\n\n if (!beforeId || !afterId) return;\n\n resetPanels(beforeId, afterId, props.resetBehavior);\n}\n\nfunction handleKeyDown (event) {\n keyboard.handleKeyDown(event);\n}\n\nfunction handleFocusEvent () {\n keyboard.handleFocus();\n}\n\nfunction handleBlurEvent () {\n keyboard.handleBlur();\n}\n</script>\n"],"mappings":"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"}