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