mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
improve HTMLOption and HTMLOptionCollection
This commit is contained in:
@@ -158,6 +158,7 @@
|
||||
{
|
||||
const sel = $('#select1')
|
||||
const opts = sel.options
|
||||
testing.expectEqual(3, sel.length)
|
||||
testing.expectEqual(3, opts.length)
|
||||
testing.expectEqual('HTMLOptionsCollection', opts.constructor.name)
|
||||
|
||||
@@ -165,6 +166,9 @@
|
||||
testing.expectEqual('val1', opts[0].value)
|
||||
testing.expectEqual('val2', opts[1].value)
|
||||
testing.expectEqual('val3', opts[2].value)
|
||||
testing.expectEqual('val1', opts.item(0).value);
|
||||
testing.expectEqual('val2', opts.item(1).value);
|
||||
testing.expectEqual('val3', opts.item(2).value);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -224,6 +228,12 @@
|
||||
testing.expectEqual(2, opts.length)
|
||||
testing.expectEqual('zero', opts[0].value)
|
||||
testing.expectEqual('b', opts[1].value)
|
||||
|
||||
opts.add(opt1, 0)
|
||||
testing.expectEqual(3, opts.length)
|
||||
testing.expectEqual('a', opts[0].value)
|
||||
testing.expectEqual('zero', opts[1].value)
|
||||
testing.expectEqual('b', opts[2].value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -364,3 +374,16 @@
|
||||
testing.expectTrue(select.outerHTML.includes('size="7"'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<select id="no_value">
|
||||
<option>d1
|
||||
<option>d2
|
||||
</select>
|
||||
<script id="no_value_attribute">
|
||||
{
|
||||
const select = $('#no_value');
|
||||
testing.expectEqual(2, select.length)
|
||||
testing.expectEqual('d1', select.options[0].value)
|
||||
testing.expectEqual('d2', select.options[1].value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,6 +20,7 @@ const std = @import("std");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const Node = @import("../Node.zig");
|
||||
const Element = @import("../Element.zig");
|
||||
const HTMLCollection = @import("HTMLCollection.zig");
|
||||
const NodeLive = @import("node_live.zig").NodeLive;
|
||||
@@ -59,17 +60,28 @@ pub fn setSelectedIndex(self: *HTMLOptionsCollection, index: i32) !void {
|
||||
|
||||
const Option = @import("../element/html/Option.zig");
|
||||
|
||||
const AddBeforeOption = union(enum) {
|
||||
option: *Option,
|
||||
index: u32,
|
||||
};
|
||||
|
||||
// Add a new option element
|
||||
pub fn add(self: *HTMLOptionsCollection, element: *Option, before: ?*Option, page: *Page) !void {
|
||||
pub fn add(self: *HTMLOptionsCollection, element: *Option, before_: ?AddBeforeOption, page: *Page) !void {
|
||||
const select_node = self._select.asNode();
|
||||
const element_node = element.asElement().asNode();
|
||||
|
||||
if (before) |before_option| {
|
||||
const before_node = before_option.asElement().asNode();
|
||||
_ = try select_node.insertBefore(element_node, before_node, page);
|
||||
} else {
|
||||
_ = try select_node.appendChild(element_node, page);
|
||||
var before_node: ?*Node = null;
|
||||
if (before_) |before| {
|
||||
switch (before) {
|
||||
.index => |idx| {
|
||||
if (self.getAtIndex(idx, page)) |el| {
|
||||
before_node = el.asNode();
|
||||
}
|
||||
},
|
||||
.option => |before_option| before_node = before_option.asNode(),
|
||||
}
|
||||
}
|
||||
_ = try select_node.insertBefore(element_node, before_node, page);
|
||||
}
|
||||
|
||||
// Remove an option element by index
|
||||
|
||||
@@ -28,7 +28,6 @@ const Option = @This();
|
||||
|
||||
_proto: *HtmlElement,
|
||||
_value: ?[]const u8 = null,
|
||||
_text: ?[]const u8 = null,
|
||||
_selected: bool = false,
|
||||
_default_selected: bool = false,
|
||||
_disabled: bool = false,
|
||||
@@ -43,9 +42,15 @@ pub fn asNode(self: *Option) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getValue(self: *const Option) []const u8 {
|
||||
// If value attribute exists, use that; otherwise use text content
|
||||
return self._value orelse self._text orelse "";
|
||||
pub fn getValue(self: *Option, page: *Page) []const u8 {
|
||||
// If value attribute exists, use that; otherwise use text content (stripped)
|
||||
if (self._value) |v| {
|
||||
return v;
|
||||
}
|
||||
|
||||
const node = self.asNode();
|
||||
const text = node.getTextContentAlloc(page.call_arena) catch return "";
|
||||
return std.mem.trim(u8, text, &std.ascii.whitespace);
|
||||
}
|
||||
|
||||
pub fn setValue(self: *Option, value: []const u8, page: *Page) !void {
|
||||
@@ -55,7 +60,9 @@ pub fn setValue(self: *Option, value: []const u8, page: *Page) !void {
|
||||
}
|
||||
|
||||
pub fn getText(self: *const Option) []const u8 {
|
||||
return self._text orelse "";
|
||||
const node: *Node = @constCast(self.asConstElement().asConstNode());
|
||||
const allocator = std.heap.page_allocator; // TODO: use proper allocator
|
||||
return node.getTextContentAlloc(allocator) catch "";
|
||||
}
|
||||
|
||||
pub fn getSelected(self: *const Option) bool {
|
||||
@@ -112,8 +119,6 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const Build = struct {
|
||||
const CData = @import("../../CData.zig");
|
||||
|
||||
pub fn created(node: *Node, _: *Page) !void {
|
||||
var self = node.as(Option);
|
||||
const element = self.asElement();
|
||||
@@ -129,17 +134,6 @@ pub const Build = struct {
|
||||
self._disabled = element.getAttributeSafe("disabled") != null;
|
||||
}
|
||||
|
||||
pub fn complete(node: *Node, _: *const Page) !void {
|
||||
var self = node.as(Option);
|
||||
|
||||
// Get text content
|
||||
if (node.firstChild()) |child| {
|
||||
if (child.is(CData.Text)) |txt| {
|
||||
self._text = txt.getWholeText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attributeChange(element: *Element, name: []const u8, value: []const u8, _: *Page) !void {
|
||||
const attribute = std.meta.stringToEnum(enum { value, selected }, name) orelse return;
|
||||
const self = element.as(Option);
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn asConstNode(self: *const Select) *const Node {
|
||||
return self.asConstElement().asConstNode();
|
||||
}
|
||||
|
||||
pub fn getValue(self: *Select) []const u8 {
|
||||
pub fn getValue(self: *Select, page: *Page) []const u8 {
|
||||
// Return value of first selected option, or first option if none selected
|
||||
var first_option: ?*Option = null;
|
||||
var iter = self.asNode().childrenIterator();
|
||||
@@ -54,25 +54,24 @@ pub fn getValue(self: *Select) []const u8 {
|
||||
first_option = option;
|
||||
}
|
||||
if (option.getSelected()) {
|
||||
return option.getValue();
|
||||
return option.getValue(page);
|
||||
}
|
||||
}
|
||||
// No explicitly selected option, return first option's value
|
||||
if (first_option) |opt| {
|
||||
return opt.getValue();
|
||||
return opt.getValue(page);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
pub fn setValue(self: *Select, value: []const u8, page: *Page) !void {
|
||||
_ = page;
|
||||
// Find option with matching value and select it
|
||||
// Note: This updates the current state (_selected), not the default state (attribute)
|
||||
// Setting value always deselects all others, even for multiple selects
|
||||
var iter = self.asNode().childrenIterator();
|
||||
while (iter.next()) |child| {
|
||||
const option = child.is(Option) orelse continue;
|
||||
option._selected = std.mem.eql(u8, option.getValue(), value);
|
||||
option._selected = std.mem.eql(u8, option.getValue(page), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +195,17 @@ pub fn getOptions(self: *Select, page: *Page) !*collections.HTMLOptionsCollectio
|
||||
});
|
||||
}
|
||||
|
||||
pub fn getLength(self: *Select) u32 {
|
||||
var i: u32 = 0;
|
||||
var it = self.asNode().childrenIterator();
|
||||
while (it.next()) |child| {
|
||||
if (child.is(Option) != null) {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
pub fn getSelectedOptions(self: *Select, page: *Page) !collections.NodeLive(.selected_options) {
|
||||
return collections.NodeLive(.selected_options).init(null, self.asNode(), {}, page);
|
||||
}
|
||||
@@ -243,6 +253,7 @@ pub const JsApi = struct {
|
||||
pub const selectedOptions = bridge.accessor(Select.getSelectedOptions, null, .{});
|
||||
pub const form = bridge.accessor(Select.getForm, null, .{});
|
||||
pub const size = bridge.accessor(Select.getSize, Select.setSize, .{});
|
||||
pub const length = bridge.accessor(Select.getLength, null, .{});
|
||||
};
|
||||
|
||||
pub const Build = struct {
|
||||
|
||||
Reference in New Issue
Block a user