Files
supabase/packages/ui-patterns/CommandMenu/internal/state/commandsState.ts
T
Charis 4fbcd61e95 feat: new command menu pt 1 (#23699)
Introduces the first (very small) slice of the new command menu as a `ui-pattern`.

This slice just handles state for available commands. It isn't hooked up to anything yet.
2024-06-04 14:32:13 -04:00

77 lines
2.3 KiB
TypeScript

import { proxy } from 'valtio'
import { type ICommand } from '../Command'
import { type ICommandSection, section$new } from '../CommandSection'
type OrderSectionInstruction = (sections: ICommandSection[], idx: number) => ICommandSection[]
type OrderCommandsInstruction = (
commands: ICommand[],
commandsToInsert: ICommand[]
) => Array<ICommand>
type CommandOptions = {
deps?: any[]
enabled?: boolean
forceMountSection?: boolean
orderSection?: OrderSectionInstruction
orderCommands?: OrderCommandsInstruction
}
type ICommandsState = {
commandSections: ICommandSection[]
registerSection: (
sectionName: string,
commands: ICommand[],
options?: CommandOptions
) => () => void
}
const initCommandsState = () => {
const state: ICommandsState = proxy({
commandSections: [],
registerSection: (sectionName, commands, options) => {
let editIndex = state.commandSections.findIndex((section) => section.name === sectionName)
if (editIndex === -1) editIndex = state.commandSections.length
state.commandSections[editIndex] ??= section$new(sectionName)
if (options?.forceMountSection) state.commandSections[editIndex].forceMount = true
if (options?.orderCommands) {
state.commandSections[editIndex].commands = options.orderCommands(
state.commandSections[editIndex].commands,
commands
)
} else {
state.commandSections[editIndex].commands.push(...commands)
}
state.commandSections =
options?.orderSection?.(state.commandSections, editIndex) ?? state.commandSections
return () => {
const idx = state.commandSections.findIndex((section) => section.name === sectionName)
if (idx !== -1) {
const filteredCommands = state.commandSections[idx].commands.filter(
(command) => !commands.map((cmd) => cmd.id).includes(command.id)
)
if (!filteredCommands.length) {
state.commandSections.splice(idx, 1)
} else {
state.commandSections[idx].commands = filteredCommands
}
}
}
},
})
return state
}
const orderSectionFirst = (sections: ICommandSection[], idx: number) => [
sections[idx],
...sections.slice(0, idx),
...sections.slice(idx + 1),
]
export { initCommandsState, orderSectionFirst }
export type { ICommandsState, CommandOptions }