mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1587 from lightpanda-io/label_control
Add HTMLLabelElement.control getter
This commit is contained in:
@@ -16,3 +16,59 @@
|
|||||||
testing.expectEqual('', l2.htmlFor);
|
testing.expectEqual('', l2.htmlFor);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<label id="l2" for="input1"><span>Name</span></label>
|
||||||
|
<input id="input2" type="text">
|
||||||
|
<input id="input-hidden" type="hidden">
|
||||||
|
<select id="sel1"><option>a</option></select>
|
||||||
|
<button id="btn1">Click</button>
|
||||||
|
<label id="l3"><input id="input3"><span>desc</span></label>
|
||||||
|
<label id="l4"><span>no control here</span></label>
|
||||||
|
<label id="l5"><label id="l5-inner"><input id="input5"></label></label>
|
||||||
|
|
||||||
|
<script id="control">
|
||||||
|
{
|
||||||
|
// for attribute pointing to a text input
|
||||||
|
const l2 = document.getElementById('l2');
|
||||||
|
testing.expectEqual('input1', l2.control.id);
|
||||||
|
|
||||||
|
// for attribute pointing to a non-existent id
|
||||||
|
const lMissing = document.createElement('label');
|
||||||
|
lMissing.htmlFor = 'does-not-exist';
|
||||||
|
testing.expectEqual(null, lMissing.control);
|
||||||
|
|
||||||
|
// for attribute pointing to a hidden input -> not labelable, returns null
|
||||||
|
const lHidden = document.createElement('label');
|
||||||
|
lHidden.htmlFor = 'input-hidden';
|
||||||
|
document.body.appendChild(lHidden);
|
||||||
|
testing.expectEqual(null, lHidden.control);
|
||||||
|
|
||||||
|
// for attribute pointing to a select
|
||||||
|
const lSel = document.createElement('label');
|
||||||
|
lSel.htmlFor = 'sel1';
|
||||||
|
document.body.appendChild(lSel);
|
||||||
|
testing.expectEqual('sel1', lSel.control.id);
|
||||||
|
|
||||||
|
// for attribute pointing to a button
|
||||||
|
const lBtn = document.createElement('label');
|
||||||
|
lBtn.htmlFor = 'btn1';
|
||||||
|
document.body.appendChild(lBtn);
|
||||||
|
testing.expectEqual('btn1', lBtn.control.id);
|
||||||
|
|
||||||
|
// no for attribute: first labelable descendant
|
||||||
|
const l3 = document.getElementById('l3');
|
||||||
|
testing.expectEqual('input3', l3.control.id);
|
||||||
|
|
||||||
|
// no for attribute: no labelable descendant -> null
|
||||||
|
const l4 = document.getElementById('l4');
|
||||||
|
testing.expectEqual(null, l4.control);
|
||||||
|
|
||||||
|
// no for attribute: nested labels, first labelable in tree order
|
||||||
|
const l5 = document.getElementById('l5');
|
||||||
|
testing.expectEqual('input5', l5.control.id);
|
||||||
|
|
||||||
|
// label with no for and not in document -> null
|
||||||
|
const lDetached = document.createElement('label');
|
||||||
|
testing.expectEqual(null, lDetached.control);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const Page = @import("../../../Page.zig");
|
|||||||
const Node = @import("../../Node.zig");
|
const Node = @import("../../Node.zig");
|
||||||
const Element = @import("../../Element.zig");
|
const Element = @import("../../Element.zig");
|
||||||
const HtmlElement = @import("../Html.zig");
|
const HtmlElement = @import("../Html.zig");
|
||||||
|
const TreeWalker = @import("../../TreeWalker.zig");
|
||||||
|
|
||||||
const Label = @This();
|
const Label = @This();
|
||||||
|
|
||||||
@@ -23,6 +24,33 @@ pub fn setHtmlFor(self: *Label, value: []const u8, page: *Page) !void {
|
|||||||
try self.asElement().setAttributeSafe(comptime .wrap("for"), .wrap(value), page);
|
try self.asElement().setAttributeSafe(comptime .wrap("for"), .wrap(value), page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getControl(self: *Label, page: *Page) ?*Element {
|
||||||
|
if (self.asElement().getAttributeSafe(comptime .wrap("for"))) |id| {
|
||||||
|
const el = page.document.getElementById(id, page) orelse return null;
|
||||||
|
if (!isLabelable(el)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tw = TreeWalker.FullExcludeSelf.Elements.init(self.asNode(), .{});
|
||||||
|
while (tw.next()) |el| {
|
||||||
|
if (isLabelable(el)) {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn isLabelable(el: *Element) bool {
|
||||||
|
const html = el.is(HtmlElement) orelse return false;
|
||||||
|
return switch (html._type) {
|
||||||
|
.button, .meter, .output, .progress, .select, .textarea => true,
|
||||||
|
.input => |input| input._input_type != .hidden,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(Label);
|
pub const bridge = js.Bridge(Label);
|
||||||
|
|
||||||
@@ -33,6 +61,7 @@ pub const JsApi = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{});
|
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{});
|
||||||
|
pub const control = bridge.accessor(Label.getControl, null, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../../../testing.zig");
|
const testing = @import("../../../../testing.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user