CSS selectors in select() Lua API // fix class attr functions
This commit is contained in:
@@ -137,6 +137,7 @@ const config: Config = {
|
||||
prism: {
|
||||
theme: prismThemes.github,
|
||||
darkTheme: prismThemes.dracula,
|
||||
additionalLanguages: ['lua'],
|
||||
},
|
||||
} satisfies Preset.ThemeConfig,
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ class CSSStylesheet:
|
||||
|
||||
match rule.selector_type:
|
||||
"simple":
|
||||
return matches_simple_selector(rule.selector_parts[0], tag_name, cls_names)
|
||||
return matches_simple_selector_with_element(rule.selector_parts[0], element)
|
||||
"descendant":
|
||||
return matches_descendant_selector(rule.selector_parts, element)
|
||||
"child":
|
||||
@@ -130,16 +130,33 @@ class CSSStylesheet:
|
||||
if selector.begins_with("."):
|
||||
var cls = selector.substr(1)
|
||||
return cls in cls_names
|
||||
elif selector.begins_with("#"):
|
||||
# need access to the element to check its ID
|
||||
return false # will be handled by matches_simple_selector_with_element
|
||||
else:
|
||||
return selector == tag_name
|
||||
|
||||
func matches_simple_selector_with_element(selector: String, element: HTMLParser.HTMLElement) -> bool:
|
||||
if not element:
|
||||
return false
|
||||
|
||||
if selector.begins_with("."):
|
||||
var cls = selector.substr(1)
|
||||
var cls_names = HTMLParser.extract_class_names(element)
|
||||
return cls in cls_names
|
||||
elif selector.begins_with("#"):
|
||||
var element_id = selector.substr(1)
|
||||
return element.get_attribute("id") == element_id
|
||||
else:
|
||||
return selector == element.tag_name
|
||||
|
||||
func matches_descendant_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool:
|
||||
if not element or parts.size() < 2:
|
||||
return false
|
||||
|
||||
# Last part should match current element
|
||||
var last_part = parts[-1].strip_edges()
|
||||
if not matches_simple_selector(last_part, element.tag_name, get_element_class_names(element)):
|
||||
if not matches_simple_selector_with_element(last_part, element):
|
||||
return false
|
||||
|
||||
# Check ancestors for remaining parts
|
||||
@@ -148,7 +165,7 @@ class CSSStylesheet:
|
||||
|
||||
while current_element and part_index >= 0:
|
||||
var part = parts[part_index].strip_edges()
|
||||
if matches_simple_selector(part, current_element.tag_name, get_element_class_names(current_element)):
|
||||
if matches_simple_selector_with_element(part, current_element):
|
||||
part_index -= 1
|
||||
if part_index < 0:
|
||||
return true
|
||||
@@ -164,11 +181,11 @@ class CSSStylesheet:
|
||||
var parent_part = parts[0].strip_edges()
|
||||
|
||||
# Element must match the child part
|
||||
if not matches_simple_selector(child_part, element.tag_name, get_element_class_names(element)):
|
||||
if not matches_simple_selector_with_element(child_part, element):
|
||||
return false
|
||||
|
||||
# Parent must match the parent part
|
||||
return matches_simple_selector(parent_part, element.parent.tag_name, get_element_class_names(element.parent))
|
||||
return matches_simple_selector_with_element(parent_part, element.parent)
|
||||
|
||||
func matches_adjacent_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool:
|
||||
if not element or not element.parent or parts.size() != 2:
|
||||
@@ -177,7 +194,7 @@ class CSSStylesheet:
|
||||
var second_part = parts[1].strip_edges()
|
||||
var first_part = parts[0].strip_edges()
|
||||
|
||||
if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)):
|
||||
if not matches_simple_selector_with_element(second_part, element):
|
||||
return false
|
||||
|
||||
# Find previous sibling
|
||||
@@ -187,7 +204,7 @@ class CSSStylesheet:
|
||||
return false
|
||||
|
||||
var prev_sibling = siblings[element_index - 1]
|
||||
return matches_simple_selector(first_part, prev_sibling.tag_name, get_element_class_names(prev_sibling))
|
||||
return matches_simple_selector_with_element(first_part, prev_sibling)
|
||||
|
||||
func matches_general_sibling_selector(parts: Array, element: HTMLParser.HTMLElement) -> bool:
|
||||
if not element or not element.parent or parts.size() != 2:
|
||||
@@ -196,7 +213,7 @@ class CSSStylesheet:
|
||||
var second_part = parts[1].strip_edges()
|
||||
var first_part = parts[0].strip_edges()
|
||||
|
||||
if not matches_simple_selector(second_part, element.tag_name, get_element_class_names(element)):
|
||||
if not matches_simple_selector_with_element(second_part, element):
|
||||
return false
|
||||
|
||||
# Check all previous siblings
|
||||
@@ -205,7 +222,7 @@ class CSSStylesheet:
|
||||
|
||||
for i in range(element_index):
|
||||
var sibling = siblings[i]
|
||||
if matches_simple_selector(first_part, sibling.tag_name, get_element_class_names(sibling)):
|
||||
if matches_simple_selector_with_element(first_part, sibling):
|
||||
return true
|
||||
|
||||
return false
|
||||
@@ -218,7 +235,7 @@ class CSSStylesheet:
|
||||
var attribute_part = parts[1].strip_edges()
|
||||
|
||||
# Check if element matches
|
||||
if element_part != "" and not matches_simple_selector(element_part, tag_name, cls_names):
|
||||
if element_part != "" and not matches_simple_selector_with_element(element_part, element):
|
||||
return false
|
||||
|
||||
# Parse attribute condition
|
||||
@@ -261,17 +278,6 @@ class CSSStylesheet:
|
||||
attr_value = attr_value.substr(1, attr_value.length() - 2)
|
||||
|
||||
return {"name": attr_name, "value": attr_value}
|
||||
|
||||
func get_element_class_names(element: HTMLParser.HTMLElement) -> Array[String]:
|
||||
var class_names: Array[String] = []
|
||||
var class_attr = element.get_attribute("class")
|
||||
if class_attr.length() > 0:
|
||||
var classes = class_attr.split(" ")
|
||||
for cls in classes:
|
||||
cls = cls.strip_edges()
|
||||
if cls.length() > 0:
|
||||
class_names.append(cls)
|
||||
return class_names
|
||||
|
||||
var stylesheet: CSSStylesheet
|
||||
var css_text: String
|
||||
|
||||
@@ -142,7 +142,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = "
|
||||
|
||||
var styles = {}
|
||||
|
||||
var class_names = get_css_class_names(element)
|
||||
var class_names = extract_class_names(element)
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element))
|
||||
# Apply inline styles (higher priority) - force override CSS rules
|
||||
var inline_style = element.get_attribute("style")
|
||||
@@ -169,7 +169,7 @@ func get_element_styles_internal(element: HTMLElement, event: String = "") -> Di
|
||||
|
||||
# Apply CSS rules
|
||||
if parse_result.css_parser:
|
||||
var class_names = get_css_class_names(element)
|
||||
var class_names = extract_class_names(element)
|
||||
styles.merge(parse_result.css_parser.stylesheet.get_styles_for_element(element.tag_name, event, class_names, element))
|
||||
|
||||
# Apply inline styles (higher priority) - force override CSS rules
|
||||
@@ -227,18 +227,7 @@ func parse_inline_style_with_event(style_string: String, event: String = "") ->
|
||||
|
||||
return properties
|
||||
|
||||
func get_css_class_names(element: HTMLElement) -> Array[String]:
|
||||
var class_names: Array[String] = []
|
||||
var class_attr = element.get_attribute("class")
|
||||
if class_attr.length() > 0:
|
||||
var classes = class_attr.split(" ")
|
||||
for cls in classes:
|
||||
cls = cls.strip_edges()
|
||||
if cls.length() > 0:
|
||||
class_names.append(cls)
|
||||
return class_names
|
||||
|
||||
func extract_class_names_from_style(element: HTMLElement) -> Array[String]:
|
||||
static func extract_class_names(element: HTMLElement) -> Array[String]:
|
||||
var class_names: Array[String] = []
|
||||
var style_attr = element.get_attribute("style")
|
||||
if style_attr.length() > 0:
|
||||
@@ -281,8 +270,10 @@ func find_all_by_class(tag: String, the_class_name: String) -> Array[HTMLElement
|
||||
|
||||
var results: Array[HTMLElement] = []
|
||||
for element in parse_result.all_elements:
|
||||
if element.tag_name == tag and element.get_class_name() == the_class_name:
|
||||
results.append(element)
|
||||
if element.tag_name == tag:
|
||||
var class_names = extract_class_names(element)
|
||||
if the_class_name in class_names:
|
||||
results.append(element)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -43,21 +43,18 @@ func get_or_assign_element_id(element: HTMLParser.HTMLElement) -> String:
|
||||
func _gurt_select_handler(vm: LuauVM) -> int:
|
||||
var selector: String = vm.luaL_checkstring(1)
|
||||
|
||||
var element_id = ""
|
||||
if selector.begins_with("#"):
|
||||
element_id = selector.substr(1)
|
||||
else:
|
||||
var element = SelectorUtils.find_first_matching(selector, dom_parser.parse_result.all_elements)
|
||||
if not element:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
|
||||
var dom_node = dom_parser.parse_result.dom_nodes.get(element_id, null)
|
||||
if not dom_node:
|
||||
vm.lua_pushnil()
|
||||
return 1
|
||||
var element_id = get_or_assign_element_id(element)
|
||||
|
||||
vm.lua_newtable()
|
||||
vm.lua_pushstring(element_id)
|
||||
vm.lua_setfield(-2, "_element_id")
|
||||
vm.lua_pushstring(element.tag_name)
|
||||
vm.lua_setfield(-2, "_tag_name")
|
||||
|
||||
add_element_methods(vm)
|
||||
return 1
|
||||
@@ -66,26 +63,7 @@ func _gurt_select_handler(vm: LuauVM) -> int:
|
||||
func _gurt_select_all_handler(vm: LuauVM) -> int:
|
||||
var selector: String = vm.luaL_checkstring(1)
|
||||
|
||||
var elements: Array[HTMLParser.HTMLElement] = []
|
||||
|
||||
# Handle different selector types
|
||||
if selector.begins_with("#"):
|
||||
# ID selector - find single element
|
||||
var element_id = selector.substr(1)
|
||||
var element = dom_parser.find_by_id(element_id)
|
||||
if element:
|
||||
elements.append(element)
|
||||
LuaPrintUtils.lua_print_direct("WARNING: Using ID selector in select_all is not recommended, use select instead.")
|
||||
elif selector.begins_with("."):
|
||||
# Class selector - find all elements with class
|
||||
var cls = selector.substr(1)
|
||||
for element in dom_parser.parse_result.all_elements:
|
||||
var element_classes = CSSParser.smart_split_utility_classes(element.get_attribute("style"))
|
||||
if cls in element_classes:
|
||||
elements.append(element)
|
||||
else:
|
||||
# Tag selector - find all elements with tag name
|
||||
elements = dom_parser.find_all(selector)
|
||||
var elements = SelectorUtils.find_all_matching(selector, dom_parser.parse_result.all_elements)
|
||||
|
||||
vm.lua_newtable()
|
||||
var index = 1
|
||||
|
||||
@@ -823,7 +823,11 @@ var HTML_CONTENT = """<head>
|
||||
local mySignal = Signal.new()
|
||||
local dataSignal = Signal.new()
|
||||
local userActionSignal = Signal.new()
|
||||
|
||||
print(".container > div: ", gurt.selectAll('.container > div'))
|
||||
print(".container div: ", gurt.selectAll('.container div'))
|
||||
print("button[disabled]: ", gurt.selectAll('button[disabled]'))
|
||||
print(".container: ", gurt.selectAll('.container'))
|
||||
print("#log-area: ", gurt.selectAll('#log-area'))
|
||||
-- Get UI elements
|
||||
local logArea = gurt.select('#log-area')
|
||||
local statusDisplay = gurt.select('#status-display')
|
||||
@@ -984,6 +988,8 @@ var HTML_CONTENT = """<head>
|
||||
<li><strong>Argument Passing:</strong> Signals can pass multiple arguments to connected callbacks</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button disabled="true">Test</button>
|
||||
<button disabled="true">Test2</button>
|
||||
</div>
|
||||
</body>
|
||||
""".to_utf8_buffer()
|
||||
|
||||
30
flumi/Scripts/Utils/SelectorUtils.gd
Normal file
30
flumi/Scripts/Utils/SelectorUtils.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
class_name SelectorUtils
|
||||
extends RefCounted
|
||||
|
||||
static func match_element(selector: String, element: HTMLParser.HTMLElement) -> bool:
|
||||
if not element:
|
||||
return false
|
||||
|
||||
var rule = CSSParser.CSSRule.new()
|
||||
rule.init(selector)
|
||||
|
||||
var class_names = HTMLParser.extract_class_names(element)
|
||||
|
||||
var stylesheet = CSSParser.CSSStylesheet.new()
|
||||
return stylesheet.selector_matches(rule, element.tag_name, "", class_names, element)
|
||||
|
||||
static func find_all_matching(selector: String, elements: Array[HTMLParser.HTMLElement]) -> Array[HTMLParser.HTMLElement]:
|
||||
var matches: Array[HTMLParser.HTMLElement] = []
|
||||
|
||||
for element in elements:
|
||||
if match_element(selector, element):
|
||||
matches.append(element)
|
||||
|
||||
return matches
|
||||
|
||||
static func find_first_matching(selector: String, elements: Array[HTMLParser.HTMLElement]) -> HTMLParser.HTMLElement:
|
||||
for element in elements:
|
||||
if match_element(selector, element):
|
||||
return element
|
||||
|
||||
return null
|
||||
1
flumi/Scripts/Utils/SelectorUtils.gd.uid
Normal file
1
flumi/Scripts/Utils/SelectorUtils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ce151ks0vlr2v
|
||||
Reference in New Issue
Block a user