Working on Dropdown. Removed dynamic_get

This commit is contained in:
2025-11-25 21:28:48 +01:00
parent 52b4e6a8b6
commit 53253278b2
6 changed files with 78 additions and 51 deletions

View File

@@ -446,9 +446,6 @@
display: inline-block;
}
.mf-dropdown-wrapper:hover .mf-dropdown {
display: block;
}
.mf-dropdown {
display: none;

View File

@@ -270,7 +270,7 @@ function updateTabs(controllerId) {
/**
* Create keyboard bindings
*/
(function() {
(function () {
/**
* Global registry to store keyboard shortcuts for multiple elements
*/
@@ -342,7 +342,7 @@ function updateTabs(controllerId) {
* Create a new trie node
* @returns {Object} - New trie node
*/
function createTrieNode() {
function createTreeNode() {
return {
config: null,
combinationStr: null,
@@ -355,18 +355,19 @@ function updateTabs(controllerId) {
* @param {Object} combinations - Map of combination strings to HTMX config objects
* @returns {Object} - Root trie node
*/
function buildTrie(combinations) {
const root = createTrieNode();
function buildTree(combinations) {
const root = createTreeNode();
for (const [combinationStr, config] of Object.entries(combinations)) {
const sequence = parseCombination(combinationStr);
console.log("Parsing combination", combinationStr, "=>", sequence);
let currentNode = root;
for (const keySet of sequence) {
const key = setToKey(keySet);
if (!currentNode.children.has(key)) {
currentNode.children.set(key, createTrieNode());
currentNode.children.set(key, createTreeNode());
}
currentNode = currentNode.children.get(key);
@@ -381,12 +382,12 @@ function updateTabs(controllerId) {
}
/**
* Traverse the trie with the current snapshot history
* @param {Object} trieRoot - Root of the trie
* Traverse the tree with the current snapshot history
* @param {Object} trieRoot - Root of the tree
* @param {Array} snapshotHistory - Array of Sets representing pressed keys
* @returns {Object|null} - Current node or null if no match
*/
function traverseTrie(trieRoot, snapshotHistory) {
function traverseTree(trieRoot, snapshotHistory) {
let currentNode = trieRoot;
for (const snapshot of snapshotHistory) {
@@ -506,6 +507,7 @@ function updateTabs(controllerId) {
// Add key to current pressed keys
KeyboardRegistry.currentKeys.add(key);
console.debug("Received key", key);
// Create a snapshot of current keyboard state
const snapshot = new Set(KeyboardRegistry.currentKeys);
@@ -530,13 +532,14 @@ function updateTabs(controllerId) {
const element = document.getElementById(elementId);
if (!element) continue;
const trieRoot = data.trie;
const treeRoot = data.tree;
// Traverse the trie with current snapshot history
const currentNode = traverseTrie(trieRoot, KeyboardRegistry.snapshotHistory);
// Traverse the tree with current snapshot history
const currentNode = traverseTree(treeRoot, KeyboardRegistry.snapshotHistory);
if (!currentNode) {
// No match in this trie, continue to next element
// No match in this tree, continue to next element
console.debug("No match in tree for event", key);
continue;
}
@@ -645,19 +648,23 @@ function updateTabs(controllerId) {
* @param {string} elementId - The ID of the element
* @param {string} combinationsJson - JSON string of combinations mapping
*/
window.add_keyboard_support = function(elementId, combinationsJson) {
window.add_keyboard_support = function (elementId, combinationsJson) {
// Parse the combinations JSON
const combinations = JSON.parse(combinationsJson);
// Build trie for this element
const trie = buildTrie(combinations);
// Build tree for this element
const tree = buildTree(combinations);
// Get element reference
const element = document.getElementById(elementId);
if (!element) {
console.error("Element with ID", elementId, "not found!");
return;
}
// Add to registry
KeyboardRegistry.elements.set(elementId, {
trie: trie,
tree: tree,
element: element
});

View File

@@ -1,19 +1,59 @@
from fastcore.xml import FT
from fasthtml.components import Div
from myfasthtml.controls.BaseCommands import BaseCommands
from myfasthtml.controls.Keyboard import Keyboard
from myfasthtml.core.commands import Command
from myfasthtml.core.instances import MultipleInstance
class Commands(BaseCommands):
def toggle(self):
return Command("Toggle", "Toggle Dropdown", self._owner.toggle).htmx(target=f"#{self._owner.get_id()}-content")
def close(self):
return Command("Close", "Close Dropdown", self._owner.close).htmx(target=f"#{self._owner.get_id()}-content")
class DropdownState:
def __init__(self):
self.opened = False
class Dropdown(MultipleInstance):
def __init__(self, parent, content, button=None, _id=None):
def __init__(self, parent, content=None, button=None, _id=None):
super().__init__(parent, _id=_id)
self.button = button
self.button = Div(button) if not isinstance(button, FT) else button
self.content = content
self.commands = Commands(self)
self._state = DropdownState()
self._toggle_command = self.commands.toggle()
# attach the command to the button
self._toggle_command.bind_ft(self.button)
def toggle(self):
self._state.opened = not self._state.opened
return self._mk_content()
def close(self):
self._state.opened = False
return self._mk_content()
def _mk_content(self):
return Div(self.content,
cls=f"mf-dropdown {'is-visible' if self._state.opened else ''}",
id=f"{self._id}-content"),
def render(self):
return Div(
Div(self.button) if self.button else Div("None"),
Div(self.content, cls="mf-dropdown"),
cls="mf-dropdown-wrapper"
Div(
Div(self.button) if self.button else Div("None"),
self._mk_content(),
cls="mf-dropdown-wrapper"
),
Keyboard(self, "_keyboard").add("esc", self.commands.close()),
id=self._id
)
def __ft__(self):

View File

@@ -102,7 +102,7 @@ class TabsManager(MultipleInstance):
tab_config = self._state.tabs[tab_id]
if tab_config["component_type"] is None:
return None
return InstancesManager.dynamic_get(self, tab_config["component_type"], tab_config["component_id"])
return InstancesManager.get(self._session, tab_config["component_id"])
@staticmethod
def _get_tab_count():

View File

@@ -2,10 +2,8 @@ import logging
import uuid
from typing import Optional
from dbengine.utils import get_class
from myfasthtml.controls.helpers import Ids
from myfasthtml.core.utils import pascal_to_snake, snake_to_pascal
from myfasthtml.core.utils import pascal_to_snake
logger = logging.getLogger("InstancesManager")
@@ -215,20 +213,6 @@ class InstancesManager:
for key, instance in InstancesManager.instances.items()
if key[0] != session_id
}
@staticmethod
def dynamic_get(parent: BaseInstance, component_type: str, instance_id: str):
logger.debug(f"Dynamic get: {component_type=} {instance_id=}")
cls = InstancesManager._get_class_name(component_type)
fully_qualified_name = f"myfasthtml.controls.{cls}.{cls}"
cls = get_class(fully_qualified_name)
return cls(parent, instance_id)
@staticmethod
def _get_class_name(component_type: str) -> str:
component_type = component_type.replace("mf-", "")
component_type = snake_to_pascal(component_type)
return component_type
RootInstance = SingleInstance(None, special_session, Ids.Root)