reorder the scopes

This commit is contained in:
Boof2015
2026-03-28 02:48:56 -04:00
parent 5dc68a5f8f
commit 3024c7dcdc
5 changed files with 220 additions and 15 deletions
+30 -1
View File
@@ -13,6 +13,7 @@ export default function Strip(): JSX.Element {
const hiddenScopes = useSettingsStore((s) => s.hiddenScopes)
const scopePopouts = useSettingsStore((s) => s.scopePopouts)
const widthWeights = useSettingsStore((s) => s.widthWeights)
const moveDockedScope = useSettingsStore((s) => s.moveDockedScope)
const setScopeWidthWeight = useSettingsStore((s) => s.setScopeWidthWeight)
const popOutScope = useSettingsStore((s) => s.popOutScope)
const accent = useThemeStore((s) => s.accent)
@@ -192,7 +193,7 @@ export default function Strip(): JSX.Element {
return (
<div ref={stripRef} className="scope-strip">
<div ref={gridRef} className="scope-strip__grid" style={gridStyle}>
{dockedScopes.map((kind) => (
{dockedScopes.map((kind, index) => (
<div
key={kind}
ref={(element) => {
@@ -200,6 +201,34 @@ export default function Strip(): JSX.Element {
}}
className="scope-strip__cell"
>
{dockedScopes.length > 1 && (
<div className="scope-strip__reorder-controls">
<button
type="button"
className="scope-strip__reorder-button"
onClick={() => moveDockedScope(kind, 'left')}
aria-label={`Move ${SCOPE_LABELS[kind]} left`}
title={`Move ${SCOPE_LABELS[kind]} left`}
disabled={index === 0}
>
<span className="scope-strip__reorder-icon" aria-hidden="true">
&#8592;
</span>
</button>
<button
type="button"
className="scope-strip__reorder-button"
onClick={() => moveDockedScope(kind, 'right')}
aria-label={`Move ${SCOPE_LABELS[kind]} right`}
title={`Move ${SCOPE_LABELS[kind]} right`}
disabled={index === dockedScopes.length - 1}
>
<span className="scope-strip__reorder-icon" aria-hidden="true">
&#8594;
</span>
</button>
</div>
)}
<button
type="button"
className="scope-strip__popout-button"
+54 -9
View File
@@ -68,7 +68,7 @@ interface SettingsState {
// Actions
toggleScope: (kind: ScopeKind) => void
moveScope: (kind: ScopeKind, direction: 'left' | 'right') => void
moveDockedScope: (kind: ScopeKind, direction: 'left' | 'right') => void
setScopeWidthWeight: (kind: ScopeKind, weight: number) => void
updateScopeSettings: <K extends ScopeKind>(kind: K, settings: Partial<ScopeSettings[K]>) => void
popOutScope: (kind: ScopeKind, bounds?: WindowBounds) => void
@@ -199,6 +199,49 @@ function normalizeScopePopouts(raw: unknown): ScopePopoutStateMap {
return defaults
}
function isDockedScope(
kind: ScopeKind,
hiddenScopes: ReadonlySet<ScopeKind>,
scopePopouts: ScopePopoutStateMap,
): boolean {
return !hiddenScopes.has(kind) && !scopePopouts[kind]?.poppedOut
}
export function moveDockedScopeOrder(
scopeOrder: ScopeKind[],
hiddenScopes: ReadonlySet<ScopeKind>,
scopePopouts: ScopePopoutStateMap,
kind: ScopeKind,
direction: 'left' | 'right',
): ScopeKind[] {
const dockedScopes = scopeOrder.filter((scope) => isDockedScope(scope, hiddenScopes, scopePopouts))
const index = dockedScopes.indexOf(kind)
if (index === -1) return scopeOrder
const targetIndex = direction === 'left' ? index - 1 : index + 1
if (targetIndex < 0 || targetIndex >= dockedScopes.length) return scopeOrder
const reorderedDockedScopes = [...dockedScopes]
;[reorderedDockedScopes[index], reorderedDockedScopes[targetIndex]] = [
reorderedDockedScopes[targetIndex],
reorderedDockedScopes[index],
]
let reorderedIndex = 0
let didChange = false
const mergedOrder = scopeOrder.map((scope) => {
if (!isDockedScope(scope, hiddenScopes, scopePopouts)) return scope
const nextScope = reorderedDockedScopes[reorderedIndex] ?? scope
reorderedIndex += 1
if (nextScope !== scope) {
didChange = true
}
return nextScope
})
return didChange ? mergedOrder : scopeOrder
}
function cloneScopeSettings(settings: ScopeSettings): ScopeSettings {
return JSON.parse(JSON.stringify(settings)) as ScopeSettings
}
@@ -262,15 +305,17 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
})
},
moveScope: (kind: ScopeKind, direction: 'left' | 'right') => {
moveDockedScope: (kind: ScopeKind, direction: 'left' | 'right') => {
set((state) => {
const order = [...state.scopeOrder]
const idx = order.indexOf(kind)
if (idx === -1) return state
const swap = direction === 'left' ? idx - 1 : idx + 1
if (swap < 0 || swap >= order.length) return state
;[order[idx], order[swap]] = [order[swap], order[idx]]
const newState = { ...state, scopeOrder: order }
const nextOrder = moveDockedScopeOrder(
state.scopeOrder,
state.hiddenScopes,
state.scopePopouts,
kind,
direction,
)
if (nextOrder === state.scopeOrder) return state
const newState = { ...state, scopeOrder: nextOrder }
saveToStorage(newState as SettingsState)
return newState
})
+49 -5
View File
@@ -342,13 +342,24 @@ select {
overflow: hidden;
}
.scope-strip__popout-button {
.scope-strip__reorder-controls {
position: absolute;
bottom: 8px;
right: 8px;
left: 8px;
z-index: 3;
display: flex;
gap: 4px;
opacity: 0;
transform: translateY(-2px);
transition: opacity 120ms ease, transform 120ms ease;
pointer-events: none;
}
.scope-strip__reorder-button,
.scope-strip__popout-button {
width: 28px;
height: 28px;
padding: 0;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(8, 12, 18, 0.74);
@@ -356,24 +367,57 @@ select {
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
cursor: pointer;
opacity: 0;
transform: translateY(-2px);
transition: opacity 120ms ease, transform 120ms ease, border-color 120ms ease, color 120ms ease;
backdrop-filter: blur(14px) saturate(1.04);
-webkit-backdrop-filter: blur(14px) saturate(1.04);
}
.scope-strip__reorder-button {
display: inline-flex;
align-items: center;
justify-content: center;
}
.scope-strip__popout-button {
position: absolute;
bottom: 8px;
right: 8px;
z-index: 3;
opacity: 0;
transform: translateY(-2px);
pointer-events: none;
}
.scope-strip__cell:hover .scope-strip__reorder-controls,
.scope-strip__cell:focus-within .scope-strip__reorder-controls,
.scope-strip__cell:hover .scope-strip__popout-button,
.scope-strip__cell:focus-within .scope-strip__popout-button,
.scope-strip__popout-button:focus-visible {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.scope-strip__popout-button:hover {
.scope-strip__reorder-button:hover:not(:disabled),
.scope-strip__reorder-button:focus-visible,
.scope-strip__popout-button:hover,
.scope-strip__popout-button:focus-visible {
color: var(--accent);
border-color: rgba(var(--accent-rgb), 0.28);
}
.scope-strip__reorder-button:focus-visible,
.scope-strip__popout-button:focus-visible {
outline: none;
}
.scope-strip__reorder-button:disabled {
color: rgba(255, 255, 255, 0.32);
border-color: rgba(255, 255, 255, 0.08);
cursor: default;
}
.scope-strip__reorder-icon,
.scope-strip__popout-icon {
display: inline-flex;
align-items: center;