@featherds/dock
Advanced tools
+84
-27
@@ -7,2 +7,3 @@ import { defineComponent, useCssVars, computed, ref, watch, provide, inject, readonly, onMounted, onUnmounted, createBlock, openBlock, resolveDynamicComponent, normalizeClass, withCtx, createVNode, createElementVNode, unref, renderSlot, createTextVNode } from "vue"; | ||
| const _hoisted_1 = ["id", "aria-label"]; | ||
| const _hoisted_2 = { class: "feather-dock-resizer" }; | ||
| const _sfc_main = /* @__PURE__ */ defineComponent({ | ||
@@ -26,8 +27,9 @@ __name: "FeatherDock", | ||
| "update:dock-expanded", | ||
| "update:dock-collapsed" | ||
| "update:dock-collapsed", | ||
| "update:dock-resized" | ||
| ], | ||
| setup(__props, { emit: __emit }) { | ||
| useCssVars((_ctx) => ({ | ||
| "13ea2f12": dockWidth.value, | ||
| "f3bd3be2": dockConfig.value.isOpen ? "cubic-bezier(0, 0.8, 0.4, 1)" : "cubic-beziercubic-bezier(0, 0.8, 0.4, 1)" | ||
| "aa306c3a": dockWidth.value, | ||
| "26049220": dockConfig.value.isOpen ? "cubic-bezier(0, 0.8, 0.4, 1)" : "cubic-beziercubic-bezier(0, 0.8, 0.4, 1)" | ||
| })); | ||
@@ -45,2 +47,3 @@ const props = __props; | ||
| "dock-closed": !isDockOpen.value, | ||
| "is-resizing": isResizing.value, | ||
| [props.location]: true | ||
@@ -52,7 +55,69 @@ }; | ||
| location: props.location, | ||
| isOpen: isDockOpen.value | ||
| isOpen: isDockOpen.value, | ||
| isResizing: isResizing.value | ||
| })); | ||
| const convertToPixels = (value) => { | ||
| if (!value) return "0"; | ||
| if (typeof value === "number") return value; | ||
| if (value.endsWith("px")) return parseInt(value, 10).toString(); | ||
| const el = document.createElement("div"); | ||
| el.style.position = "absolute"; | ||
| el.style.visibility = "hidden"; | ||
| el.style.width = value; | ||
| document.body.appendChild(el); | ||
| const pixels = el.getBoundingClientRect().width; | ||
| document.body.removeChild(el); | ||
| return pixels.toString(); | ||
| }; | ||
| const expandedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.expandedWidth)}px`; | ||
| }); | ||
| const collapsedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.collapsedWidth)}px`; | ||
| }); | ||
| const currentExpandedWidth = ref(expandedWidthPx.value); | ||
| const dockWidth = computed(() => { | ||
| return dockConfig.value.isOpen ? props.expandedWidth : props.collapsedWidth; | ||
| return dockConfig.value.isOpen ? currentExpandedWidth.value : props.collapsedWidth; | ||
| }); | ||
| const isResizing = ref(false); | ||
| const startX = ref(0); | ||
| const startWidthPx = ref(0); | ||
| watch( | ||
| () => props.expandedWidth, | ||
| () => { | ||
| currentExpandedWidth.value = expandedWidthPx.value; | ||
| } | ||
| ); | ||
| const onResizerDown = (event) => { | ||
| if (event.isPrimary === false) return; | ||
| event.preventDefault(); | ||
| isResizing.value = true; | ||
| startX.value = event.clientX; | ||
| startWidthPx.value = parseInt(currentExpandedWidth.value, 10) || 0; | ||
| window.addEventListener("pointermove", onResizerMove); | ||
| window.addEventListener("pointerup", onResizerUp); | ||
| }; | ||
| const onResizerMove = (event) => { | ||
| if (!isResizing.value) return; | ||
| const clientX = event.clientX; | ||
| const delta = clientX - startX.value; | ||
| let newWidth = startWidthPx.value; | ||
| if (props.location === "left") { | ||
| newWidth = startWidthPx.value + delta; | ||
| } else { | ||
| newWidth = startWidthPx.value - delta; | ||
| } | ||
| const minWidth = parseInt(convertToPixels(props.collapsedWidth), 10) || 48; | ||
| const maxWidth = Math.max(200, window.innerWidth - 64); | ||
| newWidth = Math.max(minWidth, Math.min(maxWidth, newWidth)); | ||
| currentExpandedWidth.value = `${Math.round(newWidth)}px`; | ||
| updatePushedElement(); | ||
| }; | ||
| const onResizerUp = () => { | ||
| if (!isResizing.value) return; | ||
| isResizing.value = false; | ||
| window.removeEventListener("pointermove", onResizerMove); | ||
| window.removeEventListener("pointerup", onResizerUp); | ||
| emit("update:dock-resized", currentExpandedWidth.value); | ||
| }; | ||
| const toggleDock = () => { | ||
@@ -78,21 +143,2 @@ isDockOpen.value = !isDockOpen.value; | ||
| }; | ||
| const convertToPixels = (value) => { | ||
| if (!value) return "0"; | ||
| if (typeof value === "number") return value; | ||
| if (value.endsWith("px")) return parseInt(value, 10).toString(); | ||
| const el = document.createElement("div"); | ||
| el.style.position = "absolute"; | ||
| el.style.visibility = "hidden"; | ||
| el.style.width = value; | ||
| document.body.appendChild(el); | ||
| const pixels = el.getBoundingClientRect().width; | ||
| document.body.removeChild(el); | ||
| return pixels.toString(); | ||
| }; | ||
| const expandedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.expandedWidth)}px`; | ||
| }); | ||
| const collapsedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.collapsedWidth)}px`; | ||
| }); | ||
| const updatePushedElement = () => { | ||
@@ -113,3 +159,3 @@ if (!props.pushedSelector) return; | ||
| const isInFlow = position === "static" || position === "relative"; | ||
| const widthFromProps = dockConfig.value.isOpen ? `${parseInt(convertToPixels(props.expandedWidth)) + parseInt(convertToPixels(pushedSelectorPadding.value))}px` : `${parseInt(convertToPixels(props.collapsedWidth)) + parseInt(convertToPixels(pushedSelectorPadding.value))}px`; | ||
| const widthFromProps = dockConfig.value.isOpen ? `${parseInt(convertToPixels(currentExpandedWidth.value)) + parseInt(convertToPixels(pushedSelectorPadding.value))}px` : `${parseInt(convertToPixels(props.collapsedWidth)) + parseInt(convertToPixels(pushedSelectorPadding.value))}px`; | ||
| const widthInPx = dockConfig.value.isOpen ? expandedWidthPx.value : collapsedWidthPx.value; | ||
@@ -183,2 +229,7 @@ if (isInFlow) { | ||
| onUnmounted(() => { | ||
| if (isResizing.value) { | ||
| window.removeEventListener("pointermove", onResizerMove); | ||
| window.removeEventListener("pointerup", onResizerUp); | ||
| isResizing.value = false; | ||
| } | ||
| document.removeEventListener("keydown", handleSidebarEscape); | ||
@@ -260,3 +311,9 @@ if (props.pushedSelector) { | ||
| ], true) | ||
| ], 8, _hoisted_1) | ||
| ], 8, _hoisted_1), | ||
| createElementVNode("div", _hoisted_2, [ | ||
| createElementVNode("div", { | ||
| class: "feather-dock-resizer-handle", | ||
| onPointerdown: onResizerDown | ||
| }, null, 32) | ||
| ]) | ||
| ]), | ||
@@ -275,5 +332,5 @@ _: 3 | ||
| }; | ||
| const FeatherDock = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4407d75f"]]); | ||
| const FeatherDock = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cd4a218e"]]); | ||
| export { | ||
| FeatherDock | ||
| }; |
+88
-21
@@ -18,2 +18,3 @@ | ||
| --feather-dock-header-offset: 0px; | ||
| --feather-dock-resizer-width: 0.5rem; | ||
| @media (prefers-reduced-motion: reduce) { | ||
@@ -32,5 +33,5 @@ --feather-dock-timing: 0.1s; /* nearly instant */ | ||
| } | ||
| .feather-dock[data-v-4407d75f] { | ||
| --feather-dock-width: var(--13ea2f12); | ||
| --feather-dock-toggle-timing-fn: var(--f3bd3be2); | ||
| .feather-dock[data-v-cd4a218e] { | ||
| --feather-dock-width: var(--aa306c3a); | ||
| --feather-dock-toggle-timing-fn: var(--26049220); | ||
| position: fixed; | ||
@@ -53,6 +54,6 @@ inset: var(--feather-dock-header-offset) 0 0 0; | ||
| } | ||
| .feather-dock.right[data-v-4407d75f] { | ||
| .feather-dock.right[data-v-cd4a218e] { | ||
| inset: 0 0 0 auto; | ||
| } | ||
| .feather-dock.right .feather-dock-toggle[data-v-4407d75f] { | ||
| .feather-dock.right .feather-dock-toggle[data-v-cd4a218e] { | ||
| transform: rotate(180deg); | ||
@@ -64,7 +65,7 @@ right: calc(var(--feather-dock-width) - 1rem); | ||
| } | ||
| .feather-dock.dock-closed[data-v-4407d75f] { | ||
| .feather-dock.dock-closed[data-v-cd4a218e] { | ||
| scrollbar-width: none; | ||
| scrollbar-color: transparent transparent; | ||
| } | ||
| .feather-dock.dock-closed > .feather-dock-toggle[data-v-4407d75f] { | ||
| .feather-dock.dock-closed > .feather-dock-toggle[data-v-cd4a218e] { | ||
| position: absolute; | ||
@@ -74,6 +75,6 @@ left: calc(var(--feather-dock-width) / 2 - 1.5rem); | ||
| } | ||
| .feather-dock.dock-closed > .feather-dock-toggle[data-v-4407d75f] { | ||
| .feather-dock.dock-closed > .feather-dock-toggle[data-v-cd4a218e] { | ||
| top: var(--feather-dock-toggle-top); | ||
| } | ||
| .feather-dock > .feather-dock-toggle[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle[data-v-cd4a218e] { | ||
| position: fixed; | ||
@@ -92,29 +93,29 @@ top: calc(var(--feather-dock-toggle-top) + var(--feather-dock-header-offset)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle .ripple[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle .ripple[data-v-cd4a218e] { | ||
| background-color: var(--feather-state-color-on-neutral); | ||
| opacity: var(--feather-state-opacity-pressed-on-neutral); | ||
| } | ||
| .feather-dock > .feather-dock-toggle.selected[data-v-4407d75f], | ||
| .feather-dock > .feather-dock-toggle .selected[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle.selected[data-v-cd4a218e], | ||
| .feather-dock > .feather-dock-toggle .selected[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle.hover[data-v-4407d75f]:hover, .feather-dock > .feather-dock-toggle:hover .hover[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle.hover[data-v-cd4a218e]:hover, .feather-dock > .feather-dock-toggle:hover .hover[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle.hover:hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover .hover.selected[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle.hover:hover.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover .hover.selected[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle:focus.focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle:focus .focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused.focus[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused .focus[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle:focus.focus[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:focus .focus[data-v-cd4a218e], .feather-dock > .feather-dock-toggle.focused.focus[data-v-cd4a218e], .feather-dock > .feather-dock-toggle.focused .focus[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle:focus.focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:focus .focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused.focus.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle.focused .focus.selected[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle:focus.focus.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:focus .focus.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle.focused.focus.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle.focused .focus.selected[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle:hover:focus .focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle:hover:focus .focus.hover[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-toggle:hover:focus .focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover.selected[data-v-4407d75f], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover.selected[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-toggle:hover:focus .focus.hover.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover:focus.focus.hover.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover.focused .focus.hover.selected[data-v-cd4a218e], .feather-dock > .feather-dock-toggle:hover.focused.focus.hover.selected[data-v-cd4a218e] { | ||
| background: linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-selected-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-hover-on-neutral))), linear-gradient(rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral)), rgba(var(--feather-state-color-on-neutral-r), var(--feather-state-color-on-neutral-g), var(--feather-state-color-on-neutral-b), var(--feather-state-opacity-focus-on-neutral))), linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)); | ||
| } | ||
| .feather-dock > .feather-dock-content[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-content[data-v-cd4a218e] { | ||
| position: relative; | ||
@@ -128,7 +129,73 @@ height: 100%; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| align-items: flex-start; | ||
| } | ||
| .feather-dock > .feather-dock-content .custom-content[data-v-4407d75f] { | ||
| .feather-dock > .feather-dock-content .custom-content[data-v-cd4a218e] { | ||
| text-align: center; | ||
| padding: 1rem; | ||
| } | ||
| .feather-dock.dock-open > .feather-dock-resizer[data-v-cd4a218e] { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| /* Make the resizer easier to grab */ | ||
| width: var(--feather-dock-resizer-width); | ||
| cursor: ew-resize; | ||
| z-index: var(--feather-zindex-fixed); | ||
| left: calc(var(--feather-dock-width)); | ||
| right: auto; | ||
| border-width: 2px; | ||
| background-color: transparent; | ||
| } | ||
| .feather-dock.dock-open > .feather-dock-resizer .feather-dock-resizer-handle[data-v-cd4a218e] { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| left: 50%; | ||
| transform: translateX(-50%); | ||
| width: var(--feather-dock-resizer-width); | ||
| border-radius: 0.125rem; | ||
| background-color: transparent; | ||
| border-left: 2px solid transparent; | ||
| transition: background-color 0.25s ease-in-out 0.25s, border-left-color 0.25s ease-in-out 0.25s; | ||
| } | ||
| .feather-dock.dock-open > .feather-dock-resizer .feather-dock-resizer-handle[data-v-cd4a218e]:hover { | ||
| background-color: var(--feather-dock-background-color); | ||
| border-left: 2px solid var(--feather-dock-color); | ||
| border-right: none; | ||
| } | ||
| .feather-dock.dock-open.right > .feather-dock-resizer[data-v-cd4a218e] { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| /* Make the resizer easier to grab */ | ||
| width: var(--feather-dock-resizer-width); | ||
| cursor: ew-resize; | ||
| z-index: var(--feather-zindex-fixed); | ||
| left: auto; | ||
| right: calc(var(--feather-dock-width)); | ||
| background-color: transparent; | ||
| border-width: 2px; | ||
| background-color: transparent; | ||
| } | ||
| .feather-dock.dock-open.right > .feather-dock-resizer .feather-dock-resizer-handle[data-v-cd4a218e] { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| right: 50%; | ||
| width: var(--feather-dock-resizer-width); | ||
| border-radius: 0.125rem; | ||
| background-color: transparent; | ||
| border-right: 2px solid transparent; | ||
| transition: background-color 0.25s ease-in-out 0.25s, border-right-color 0.25s ease-in-out 0.25s; | ||
| } | ||
| .feather-dock.dock-open.right > .feather-dock-resizer .feather-dock-resizer-handle[data-v-cd4a218e]:hover { | ||
| background-color: var(--feather-dock-background-color); | ||
| border-right: 2px solid var(--feather-dock-color); | ||
| border-left: none; | ||
| } | ||
| .feather-dock.is-resizing[data-v-cd4a218e] { | ||
| transition-duration: 0s; | ||
| } | ||
| .feather-dock.is-resizing > .feather-dock-toggle[data-v-cd4a218e] { | ||
| transition-duration: 0s; | ||
| } |
+4
-4
| { | ||
| "name": "@featherds/dock", | ||
| "version": "0.12.42", | ||
| "version": "0.12.43", | ||
| "publishConfig": { | ||
@@ -12,4 +12,4 @@ "access": "public" | ||
| "dependencies": { | ||
| "@featherds/icon": "^0.12.42", | ||
| "@featherds/styles": "^0.12.42", | ||
| "@featherds/icon": "^0.12.43", | ||
| "@featherds/styles": "^0.12.43", | ||
| "vue": "^3.5.13" | ||
@@ -22,3 +22,3 @@ }, | ||
| "types": "./src/index.d.ts", | ||
| "gitHead": "e807ef35c30f4d9ef4a08ac173dfd50e3f2004d7" | ||
| "gitHead": "749671ccc8294eaf77881b9fea0e42980646e70a" | ||
| } |
@@ -42,2 +42,8 @@ <template> | ||
| </div> | ||
| <div class="feather-dock-resizer"> | ||
| <div | ||
| class="feather-dock-resizer-handle" | ||
| @pointerdown="onResizerDown" | ||
| ></div> | ||
| </div> | ||
| </component> | ||
@@ -80,2 +86,3 @@ </template> | ||
| "update:dock-collapsed", | ||
| "update:dock-resized", | ||
| ]); | ||
@@ -97,2 +104,3 @@ | ||
| "dock-closed": !isDockOpen.value, | ||
| "is-resizing": isResizing.value, | ||
| [props.location]: true, | ||
@@ -106,8 +114,104 @@ }; | ||
| isOpen: isDockOpen.value, | ||
| isResizing: isResizing.value, | ||
| })); | ||
| const convertToPixels = (value: string): string => { | ||
| if (!value) return "0"; | ||
| if (typeof value === "number") return value; | ||
| if (value.endsWith("px")) return parseInt(value, 10).toString(); | ||
| const el = document.createElement("div"); | ||
| el.style.position = "absolute"; | ||
| el.style.visibility = "hidden"; | ||
| el.style.width = value; | ||
| document.body.appendChild(el); | ||
| const pixels = el.getBoundingClientRect().width; | ||
| document.body.removeChild(el); | ||
| return pixels.toString(); | ||
| }; | ||
| const expandedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.expandedWidth)}px`; | ||
| }); | ||
| const collapsedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.collapsedWidth)}px`; | ||
| }); | ||
| // computed px values of provided widths | ||
| // (expandedWidthPx and collapsedWidthPx are declared above) | ||
| // currentExpandedWidth stores the current expanded width (in CSS units, usually px) | ||
| // and is updated when the user drags the resizer. Initialized from the prop. | ||
| const currentExpandedWidth = ref<string>(expandedWidthPx.value); | ||
| // Expose dockWidth as a CSS-ready string. When closed, use collapsed prop; when open, use currentExpandedWidth. | ||
| const dockWidth = computed(() => { | ||
| return dockConfig.value.isOpen ? props.expandedWidth : props.collapsedWidth; | ||
| return dockConfig.value.isOpen | ||
| ? currentExpandedWidth.value | ||
| : props.collapsedWidth; | ||
| }); | ||
| // Resizing state | ||
| const isResizing = ref(false); | ||
| const startX = ref(0); | ||
| const startWidthPx = ref(0); | ||
| // Keep local width in sync if prop changes externally | ||
| watch( | ||
| () => props.expandedWidth, | ||
| () => { | ||
| currentExpandedWidth.value = expandedWidthPx.value; | ||
| } | ||
| ); | ||
| // --- Resizer handlers ------------------------------------------------- | ||
| const onResizerDown = (event: PointerEvent) => { | ||
| // Only respond to primary pointer | ||
| if ((event as PointerEvent).isPrimary === false) return; | ||
| event.preventDefault(); | ||
| isResizing.value = true; | ||
| startX.value = (event as PointerEvent).clientX; | ||
| // parse start width from currentExpandedWidth (assume px or numeric) | ||
| startWidthPx.value = parseInt(currentExpandedWidth.value as string, 10) || 0; | ||
| // Add global listeners | ||
| window.addEventListener("pointermove", onResizerMove); | ||
| window.addEventListener("pointerup", onResizerUp); | ||
| }; | ||
| const onResizerMove = (event: PointerEvent) => { | ||
| if (!isResizing.value) return; | ||
| const clientX = (event as PointerEvent).clientX; | ||
| const delta = clientX - startX.value; | ||
| let newWidth = startWidthPx.value; | ||
| if (props.location === "left") { | ||
| newWidth = startWidthPx.value + delta; | ||
| } else { | ||
| newWidth = startWidthPx.value - delta; | ||
| } | ||
| // enforce minimum width = collapsedWidthPx | ||
| const minWidth = parseInt(convertToPixels(props.collapsedWidth), 10) || 48; | ||
| const maxWidth = Math.max(200, window.innerWidth - 64); | ||
| newWidth = Math.max(minWidth, Math.min(maxWidth, newWidth)); | ||
| currentExpandedWidth.value = `${Math.round(newWidth)}px`; | ||
| // Update pushed elements live | ||
| updatePushedElement(); | ||
| }; | ||
| const onResizerUp = () => { | ||
| if (!isResizing.value) return; | ||
| isResizing.value = false; | ||
| window.removeEventListener("pointermove", onResizerMove); | ||
| window.removeEventListener("pointerup", onResizerUp); | ||
| emit("update:dock-resized", currentExpandedWidth.value); | ||
| }; | ||
| const toggleDock = () => { | ||
@@ -141,27 +245,2 @@ isDockOpen.value = !isDockOpen.value; | ||
| const convertToPixels = (value: string): string => { | ||
| if (!value) return "0"; | ||
| if (typeof value === "number") return value; | ||
| if (value.endsWith("px")) return parseInt(value, 10).toString(); | ||
| const el = document.createElement("div"); | ||
| el.style.position = "absolute"; | ||
| el.style.visibility = "hidden"; | ||
| el.style.width = value; | ||
| document.body.appendChild(el); | ||
| const pixels = el.getBoundingClientRect().width; | ||
| document.body.removeChild(el); | ||
| return pixels.toString(); | ||
| }; | ||
| const expandedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.expandedWidth)}px`; | ||
| }); | ||
| const collapsedWidthPx = computed(() => { | ||
| return `${convertToPixels(props.collapsedWidth)}px`; | ||
| }); | ||
| const updatePushedElement = () => { | ||
@@ -189,5 +268,6 @@ if (!props.pushedSelector) return; | ||
| // Use the live currentExpandedWidth when the dock is open so pushed elements follow resizes | ||
| const widthFromProps = dockConfig.value.isOpen | ||
| ? `${ | ||
| parseInt(convertToPixels(props.expandedWidth)) + | ||
| parseInt(convertToPixels(currentExpandedWidth.value)) + | ||
| parseInt(convertToPixels(pushedSelectorPadding.value)) | ||
@@ -316,2 +396,8 @@ }px` | ||
| onUnmounted(() => { | ||
| // Cleanup any global pointer listeners used during resizing | ||
| if (isResizing.value) { | ||
| window.removeEventListener("pointermove", onResizerMove); | ||
| window.removeEventListener("pointerup", onResizerUp); | ||
| isResizing.value = false; | ||
| } | ||
| document.removeEventListener("keydown", handleSidebarEscape); | ||
@@ -393,2 +479,3 @@ if (props.pushedSelector) { | ||
| --feather-dock-header-offset: 0px; | ||
| --feather-dock-resizer-width: 0.5rem; | ||
@@ -477,4 +564,2 @@ @media (prefers-reduced-motion: reduce) { | ||
| transition-timing-function: var(--feather-dock-toggle-timing-fn); | ||
| // outline: 0.125rem solid var(--feather-background); | ||
| // outline-offset: -0.125rem; | ||
| font-size: 1rem; | ||
@@ -492,3 +577,3 @@ z-index: calc(var(--feather-zindex-modal) + 1); | ||
| flex-direction: column; | ||
| align-items: center; | ||
| align-items: flex-start; | ||
@@ -500,3 +585,75 @@ .custom-content { | ||
| } | ||
| &.dock-open > .feather-dock-resizer { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| /* Make the resizer easier to grab */ | ||
| width: var(--feather-dock-resizer-width); | ||
| cursor: ew-resize; | ||
| z-index: var(vars.$zindex-fixed); | ||
| left: calc(var(--feather-dock-width)); | ||
| right: auto; | ||
| border-width: 2px; | ||
| background-color: transparent; | ||
| .feather-dock-resizer-handle { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| left: 50%; | ||
| transform: translateX(-50%); | ||
| width: var(--feather-dock-resizer-width); | ||
| border-radius: 0.125rem; | ||
| background-color: transparent; | ||
| border-left: 2px solid transparent; | ||
| transition: background-color 0.25s ease-in-out 0.25s, | ||
| border-left-color 0.25s ease-in-out 0.25s; | ||
| &:hover { | ||
| background-color: var(--feather-dock-background-color); | ||
| border-left: 2px solid var(--feather-dock-color); | ||
| border-right: none; | ||
| } | ||
| } | ||
| } | ||
| &.dock-open.right > .feather-dock-resizer { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| /* Make the resizer easier to grab */ | ||
| width: var(--feather-dock-resizer-width); | ||
| cursor: ew-resize; | ||
| z-index: var(vars.$zindex-fixed); | ||
| left: auto; | ||
| right: calc(var(--feather-dock-width)); | ||
| background-color: transparent; | ||
| border-width: 2px; | ||
| background-color: transparent; | ||
| .feather-dock-resizer-handle { | ||
| position: absolute; | ||
| top: 0; | ||
| bottom: 0; | ||
| right: 50%; | ||
| width: var(--feather-dock-resizer-width); | ||
| border-radius: 0.125rem; | ||
| background-color: transparent; | ||
| border-right: 2px solid transparent; | ||
| transition: background-color 0.25s ease-in-out 0.25s, | ||
| border-right-color 0.25s ease-in-out 0.25s; | ||
| &:hover { | ||
| background-color: var(--feather-dock-background-color); | ||
| border-right: 2px solid var(--feather-dock-color); | ||
| border-left: none; | ||
| } | ||
| } | ||
| } | ||
| &.is-resizing { | ||
| transition-duration: 0s; | ||
| > .feather-dock-toggle { | ||
| transition-duration: 0s; | ||
| } | ||
| } | ||
| } | ||
| </style> |
+1
-0
@@ -7,2 +7,3 @@ export type DockLocation = "left" | "right" | "none"; | ||
| isOpen: boolean; | ||
| isResizing?: boolean; | ||
| } | ||
@@ -9,0 +10,0 @@ |
63085
17.67%692
22.05%Updated
Updated