pub const css = @import("../css_parser.zig"); const Printer = css.Printer; const PrintErr = css.PrintErr; const LengthPercentage = css.css_values.length.LengthPercentage; const VendorPrefix = css.VendorPrefix; const Property = css.Property; const Feature = css.prefixes.Feature; /// A value for the [align-content](https://www.w3.org/TR/css-align-3/#propdef-align-content) property. pub const AlignContent = union(enum) { /// Default alignment. normal: void, /// A baseline position. baseline_position: BaselinePosition, /// A content distribution keyword. content_distribution: ContentDistribution, /// A content position keyword. content_position: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, /// A content position keyword. value: ContentPosition, pub fn toInner(this: *const @This()) ContentPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn __generateToCss() void {} pub fn parse(input: *css.Parser) css.Result(@This()) { const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); const value = switch (ContentPosition.parse(input)) { .result => |v| v, .err => |e| return .{ .err = e }, }; return .{ .result = .{ .overflow = overflow, .value = value } }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, pub const parse = css.DeriveParse(@This()).parse; pub const toCss = css.DeriveToCss(@This()).toCss; pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-baseline-position) value, /// as used in the alignment properties. pub const BaselinePosition = enum { /// The first baseline. first, /// The last baseline. last, pub fn parse(input: *css.Parser) css.Result(@This()) { const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { .err => |e| return .{ .err = e }, .result => |v| v, }; const BaselinePositionIdent = enum { baseline, first, last, }; const BaselinePositionMap = bun.ComptimeEnumMap(BaselinePositionIdent); if (BaselinePositionMap.getASCIIICaseInsensitive(ident)) |value| switch (value) { .baseline => return .{ .result = BaselinePosition.first }, .first => { if (input.expectIdentMatching("baseline").asErr()) |e| return .{ .err = e }; return .{ .result = BaselinePosition.first }; }, .last => { if (input.expectIdentMatching("baseline").asErr()) |e| return .{ .err = e }; return .{ .result = BaselinePosition.last }; }, } else return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } pub fn toCss(this: *const BaselinePosition, dest: *css.Printer) css.PrintErr!void { return switch (this.*) { .first => try dest.writeStr("baseline"), .last => try dest.writeStr("last baseline"), }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [justify-content](https://www.w3.org/TR/css-align-3/#propdef-justify-content) property. pub const JustifyContent = union(enum) { /// Default justification. normal, /// A content distribution keyword. content_distribution: ContentDistribution, /// A content position keyword. content_position: struct { /// A content position keyword. value: ContentPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn toInner(this: *const @This()) ContentPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Justify to the left. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Justify to the right. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, pub fn parse(input: *css.Parser) css.Result(@This()) { if (input.tryParse(css.Parser.expectIdentMatching, .{"normal"}).isOk()) { return .{ .result = .normal }; } if (input.tryParse(ContentDistribution.parse, .{}).asValue()) |val| { return .{ .result = .{ .content_distribution = val } }; } const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); if (input.tryParse(ContentPosition.parse, .{}).asValue()) |content_position| { return .{ .result = .{ .content_position = .{ .overflow = overflow, .value = content_position, }, } }; } const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { .result => |v| v, .err => |e| return .{ .err = e }, }; const JustifyContentIdent = enum { left, right, }; const JustifyContentIdentMap = bun.ComptimeEnumMap(JustifyContentIdent); if (JustifyContentIdentMap.getASCIIICaseInsensitive(ident)) |value| return switch (value) { .left => .{ .result = .{ .left = .{ .overflow = overflow } } }, .right => .{ .result = .{ .right = .{ .overflow = overflow } } }, } else return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } pub fn toCss(this: *const @This(), dest: *css.Printer) css.PrintErr!void { return switch (this.*) { .normal => dest.writeStr("normal"), .content_distribution => |value| value.toCss(dest), .content_position => |*cp| { if (cp.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } return cp.value.toCss(dest); }, .left => |*l| { if (l.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } return dest.writeStr("left"); }, .right => |*r| { if (r.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } return dest.writeStr("right"); }, }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [align-self](https://www.w3.org/TR/css-align-3/#align-self-property) property. pub const AlignSelf = union(enum) { /// Automatic alignment. auto, /// Default alignment. normal, /// Item is stretched. stretch, /// A baseline position keyword. baseline_position: BaselinePosition, /// A self position keyword. self_position: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, /// A self position keyword. value: SelfPosition, pub fn toInner(this: *const @This()) SelfPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn __generateToCss() void {} pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn parse(input: *css.Parser) css.Result(@This()) { const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); const self_position = switch (SelfPosition.parse(input)) { .result => |v| v, .err => |e| return .{ .err = e }, }; return .{ .result = .{ .overflow = overflow, .value = self_position, }, }; } }, pub const parse = css.DeriveParse(@This()).parse; pub const toCss = css.DeriveToCss(@This()).toCss; pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } }; /// A value for the [justify-self](https://www.w3.org/TR/css-align-3/#justify-self-property) property. pub const JustifySelf = union(enum) { /// Automatic justification. auto, /// Default justification. normal, /// Item is stretched. stretch, /// A baseline position keyword. baseline_position: BaselinePosition, /// A self position keyword. self_position: struct { /// A self position keyword. value: SelfPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn toInner(this: *const @This()) SelfPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Item is justified to the left. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Item is justified to the right. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, pub fn parse(input: *css.Parser) css.Result(@This()) { if (input.tryParse(css.Parser.expectIdentMatching, .{"auto"}).isOk()) { return .{ .result = .auto }; } if (input.tryParse(css.Parser.expectIdentMatching, .{"normal"}).isOk()) { return .{ .result = .normal }; } if (input.tryParse(css.Parser.expectIdentMatching, .{"stretch"}).isOk()) { return .{ .result = .stretch }; } if (input.tryParse(BaselinePosition.parse, .{}).asValue()) |val| { return .{ .result = .{ .baseline_position = val } }; } const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); if (input.tryParse(SelfPosition.parse, .{}).asValue()) |self_position| { return .{ .result = .{ .self_position = .{ .overflow = overflow, .value = self_position } } }; } const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { .result => |v| v, .err => |e| return .{ .err = e }, }; const Enum = enum { left, right }; const Map = bun.ComptimeEnumMap(Enum); if (Map.getASCIIICaseInsensitive(ident)) |val| return .{ .result = switch (val) { .left => .{ .left = .{ .overflow = overflow } }, .right => .{ .right = .{ .overflow = overflow } }, } }; return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } pub fn toCss(this: *const JustifySelf, dest: *css.Printer) css.PrintErr!void { return switch (this.*) { .auto => try dest.writeStr("auto"), .normal => try dest.writeStr("normal"), .stretch => try dest.writeStr("stretch"), .baseline_position => |*baseline_position| baseline_position.toCss(dest), .self_position => |*self_position| { if (self_position.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try self_position.value.toCss(dest); }, .left => |*left| { if (left.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try dest.writeStr("left"); }, .right => |*right| { if (right.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try dest.writeStr("right"); }, }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [align-items](https://www.w3.org/TR/css-align-3/#align-items-property) property. pub const AlignItems = union(enum) { /// Default alignment. normal, /// Items are stretched. stretch, /// A baseline position keyword. baseline_position: BaselinePosition, /// A self position keyword. self_position: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, /// A self position keyword. value: SelfPosition, pub fn toInner(this: *const @This()) SelfPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn parse(input: *css.Parser) css.Result(@This()) { const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); const self_position = switch (SelfPosition.parse(input)) { .result => |v| v, .err => |e| return .{ .err = e }, }; return .{ .result = .{ .overflow = overflow, .value = self_position, }, }; } pub fn __generateToCss() void {} pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, pub const parse = css.DeriveParse(@This()).parse; pub const toCss = css.DeriveToCss(@This()).toCss; pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [justify-items](https://www.w3.org/TR/css-align-3/#justify-items-property) property. pub const JustifyItems = union(enum) { /// Default justification. normal, /// Items are stretched. stretch, /// A baseline position keyword. baseline_position: BaselinePosition, /// A self position keyword, with optional overflow position. self_position: struct { /// A self position keyword. value: SelfPosition, /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn toInner(this: *const @This()) SelfPositionInner { return .{ .overflow = this.overflow, .value = this.value, }; } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Items are justified to the left, with an optional overflow position. left: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// Items are justified to the right, with an optional overflow position. right: struct { /// An overflow alignment mode. overflow: ?OverflowPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }, /// A legacy justification keyword. legacy: LegacyJustify, pub fn parse(input: *css.Parser) css.Result(@This()) { if (input.tryParse(css.Parser.expectIdentMatching, .{"normal"}).isOk()) { return .{ .result = .normal }; } if (input.tryParse(css.Parser.expectIdentMatching, .{"stretch"}).isOk()) { return .{ .result = .stretch }; } if (input.tryParse(BaselinePosition.parse, .{}).asValue()) |val| { return .{ .result = .{ .baseline_position = val } }; } if (input.tryParse(LegacyJustify.parse, .{}).asValue()) |val| { return .{ .result = .{ .legacy = val } }; } const overflow = input.tryParse(OverflowPosition.parse, .{}).asValue(); if (input.tryParse(SelfPosition.parse, .{}).asValue()) |self_position| { return .{ .result = .{ .self_position = .{ .overflow = overflow, .value = self_position } } }; } const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { .result => |v| v, .err => |e| return .{ .err = e }, }; const Enum = enum { left, right }; const Map = bun.ComptimeEnumMap(Enum); if (Map.getASCIIICaseInsensitive(ident)) |val| return .{ .result = switch (val) { .left => .{ .left = .{ .overflow = overflow } }, .right => .{ .right = .{ .overflow = overflow } }, } }; return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } pub fn toCss(this: *const JustifyItems, dest: *css.Printer) css.PrintErr!void { switch (this.*) { .normal => try dest.writeStr("normal"), .stretch => try dest.writeStr("stretch"), .baseline_position => |*val| try val.toCss(dest), .self_position => |*sp| { if (sp.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try sp.value.toCss(dest); }, .left => |*l| { if (l.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try dest.writeStr("left"); }, .right => |*r| { if (r.overflow) |*overflow| { try overflow.toCss(dest); try dest.writeStr(" "); } try dest.writeStr("right"); }, .legacy => |l| try l.toCss(dest), } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A legacy justification keyword, as used in the `justify-items` property. pub const LegacyJustify = enum { /// Left justify. left, /// Right justify. right, /// Centered. center, pub fn parse(input: *css.Parser) css.Result(@This()) { const location = input.currentSourceLocation(); const ident = switch (input.expectIdent()) { .err => |e| return .{ .err = e }, .result => |v| v, }; const LegacyJustifyIdent = enum { legacy, left, right, center, }; const LegacyJustifyMap = bun.ComptimeEnumMap(LegacyJustifyIdent); if (LegacyJustifyMap.getASCIIICaseInsensitive(ident)) |value| { switch (value) { .legacy => { const inner_location = input.currentSourceLocation(); const inner_ident = switch (input.expectIdent()) { .err => |e| return .{ .err = e }, .result => |v| v, }; const InnerEnum = enum { left, right, center }; const InnerLegacyJustifyMap = bun.ComptimeEnumMap(InnerEnum); if (InnerLegacyJustifyMap.getASCIIICaseInsensitive(inner_ident)) |inner_value| { return switch (inner_value) { .left => .{ .result = .left }, .right => .{ .result = .right }, .center => .{ .result = .center }, }; } else { return .{ .err = inner_location.newUnexpectedTokenError(.{ .ident = inner_ident }) }; } }, .left => { if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; return .{ .result = .left }; }, .right => { if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; return .{ .result = .right }; }, .center => { if (input.expectIdentMatching("legacy").asErr()) |e| return .{ .err = e }; return .{ .result = .center }; }, } } return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } pub fn toCss(this: *const @This(), dest: *css.Printer) css.PrintErr!void { try dest.writeStr("legacy "); switch (this.*) { .left => try dest.writeStr("left"), .right => try dest.writeStr("right"), .center => try dest.writeStr("center"), } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A [gap](https://www.w3.org/TR/css-align-3/#column-row-gap) value, as used in the /// `column-gap` and `row-gap` properties. pub const GapValue = union(enum) { /// Equal to `1em` for multi-column containers, and zero otherwise. normal, /// An explicit length. length_percentage: LengthPercentage, pub const parse = css.DeriveParse(@This()).parse; pub const toCss = css.DeriveToCss(@This()).toCss; pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [gap](https://www.w3.org/TR/css-align-3/#gap-shorthand) shorthand property. pub const Gap = struct { /// The row gap. row: GapValue, /// The column gap. column: GapValue, pub const PropertyFieldMap = .{ .row = "row-gap", .column = "column-gap", }; pub fn parse(input: *css.Parser) css.Result(@This()) { const row = switch (@call(.auto, @field(GapValue, "parse"), .{input})) { .result => |v| v, .err => |e| return .{ .err = e }, }; const column = switch (input.tryParse(@field(GapValue, "parse"), .{})) { .result => |v| v, .err => row, }; return .{ .result = .{ .row = row, .column = column } }; } pub fn toCss(this: *const Gap, dest: *css.Printer) css.PrintErr!void { try this.row.toCss(dest); if (!this.column.eql(&this.row)) { try dest.writeStr(" "); try this.column.toCss(dest); } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [place-items](https://www.w3.org/TR/css-align-3/#place-items-property) shorthand property. pub const PlaceItems = struct { /// The item alignment. @"align": AlignItems, /// The item justification. justify: JustifyItems, pub const PropertyFieldMap = .{ .@"align" = "align-items", .justify = "justify-items", }; pub const VendorPrefixMap = .{ .@"align" = true, }; pub fn parse(input: *css.Parser) css.Result(@This()) { const @"align" = switch (@call(.auto, @field(AlignItems, "parse"), .{input})) { .result => |v| v, .err => |e| return .{ .err = e }, }; const justify = switch (input.tryParse(@field(JustifyItems, "parse"), .{})) { .result => |v| v, .err => switch (@"align") { .normal => JustifyItems.normal, .stretch => JustifyItems.stretch, .baseline_position => |p| JustifyItems{ .baseline_position = p }, .self_position => |sp| JustifyItems{ .self_position = .{ .overflow = if (sp.overflow) |o| o else null, .value = sp.value, }, }, }, }; return .{ .result = .{ .@"align" = @"align", .justify = justify } }; } pub fn toCss(this: *const PlaceItems, dest: *css.Printer) css.PrintErr!void { try this.@"align".toCss(dest); const is_equal = switch (this.justify) { .normal => this.@"align".eql(&AlignItems{ .normal = {} }), .stretch => this.@"align".eql(&AlignItems{ .stretch = {} }), .baseline_position => |*p| brk: { if (this.@"align" == .baseline_position) break :brk p.eql(&this.@"align".baseline_position); break :brk false; }, .self_position => |*p| brk: { if (this.@"align" == .self_position) break :brk p.toInner().eql(&this.@"align".self_position.toInner()); break :brk false; }, else => false, }; if (!is_equal) { try dest.writeStr(" "); try this.justify.toCss(dest); } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A value for the [place-self](https://www.w3.org/TR/css-align-3/#place-self-property) shorthand property. pub const PlaceSelf = struct { /// The item alignment. @"align": AlignSelf, /// The item justification. justify: JustifySelf, pub const PropertyFieldMap = .{ .@"align" = "align-self", .justify = "justify-self", }; pub const VendorPrefixMap = .{ .@"align" = true, }; pub fn parse(input: *css.Parser) css.Result(@This()) { const @"align" = switch (@call(.auto, @field(AlignSelf, "parse"), .{input})) { .result => |v| v, .err => |e| return .{ .err = e }, }; const justify = switch (input.tryParse(@field(JustifySelf, "parse"), .{})) { .result => |v| v, .err => switch (@"align") { .auto => JustifySelf.auto, .normal => JustifySelf.normal, .stretch => JustifySelf.stretch, .baseline_position => |p| JustifySelf{ .baseline_position = p }, .self_position => |sp| JustifySelf{ .self_position = .{ .overflow = if (sp.overflow) |o| o else null, .value = sp.value, }, }, }, }; return .{ .result = .{ .@"align" = @"align", .justify = justify } }; } pub fn toCss(this: *const PlaceSelf, dest: *css.Printer) css.PrintErr!void { try this.@"align".toCss(dest); const is_equal = switch (this.justify) { .auto => true, .normal => this.@"align" == .normal, .stretch => this.@"align" == .stretch, .baseline_position => |p| switch (this.@"align") { .baseline_position => |p2| p.eql(&p2), else => false, }, .self_position => |sp| brk: { if (this.@"align" == .self_position) break :brk sp.toInner().eql(&this.@"align".self_position.toInner()); break :brk false; }, else => false, }; if (!is_equal) { try dest.writeStr(" "); try this.justify.toCss(dest); } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-self-position) value. pub const SelfPosition = enum { /// Item is centered within the container. center, /// Item is aligned to the start of the container. start, /// Item is aligned to the end of the container. end, /// Item is aligned to the edge of the container corresponding to the start side of the item. @"self-start", /// Item is aligned to the edge of the container corresponding to the end side of the item. @"self-end", /// Item is aligned to the start of the container, within flexbox layouts. @"flex-start", /// Item is aligned to the end of the container, within flexbox layouts. @"flex-end", const css_impl = css.DefineEnumProperty(@This()); pub const eql = css_impl.eql; pub const hash = css_impl.hash; pub const parse = css_impl.parse; pub const toCss = css_impl.toCss; pub const deepClone = css_impl.deepClone; }; /// A value for the [place-content](https://www.w3.org/TR/css-align-3/#place-content) shorthand property. pub const PlaceContent = struct { /// The content alignment. @"align": AlignContent, /// The content justification. justify: JustifyContent, pub const PropertyFieldMap = .{ .@"align" = css.PropertyIdTag.@"align-content", .justify = css.PropertyIdTag.@"justify-content", }; pub const VendorPrefixMap = .{ .@"align" = true, .justify = true, }; pub fn parse(input: *css.Parser) css.Result(@This()) { const @"align" = switch (@call(.auto, @field(AlignContent, "parse"), .{input})) { .result => |v| v, .err => |e| return .{ .err = e }, }; const justify = switch (@call(.auto, @field(JustifyContent, "parse"), .{input})) { .result => |v| v, .err => |_| switch (@"align") { .baseline_position => JustifyContent{ .content_position = .{ .overflow = null, .value = .start, } }, .normal => JustifyContent.normal, .content_distribution => |value| JustifyContent{ .content_distribution = value }, .content_position => |pos| JustifyContent{ .content_position = .{ .overflow = if (pos.overflow) |*overflow| overflow.deepClone(input.allocator()) else null, .value = pos.value.deepClone(input.allocator()), } }, }, }; return .{ .result = .{ .@"align" = @"align", .justify = justify } }; } pub fn toCss(this: *const PlaceContent, dest: *css.Printer) css.PrintErr!void { try this.@"align".toCss(dest); const is_equal = switch (this.justify) { .normal => brk: { if (this.@"align" == .normal) break :brk true; break :brk false; }, .content_distribution => |*d| brk: { if (this.@"align" == .content_distribution) break :brk d.eql(&this.@"align".content_distribution); break :brk false; }, .content_position => |*p| brk: { if (this.@"align" == .content_position) break :brk p.toInner().eql(&this.@"align".content_position.toInner()); break :brk false; }, else => false, }; if (!is_equal) { try dest.writeStr(" "); try this.justify.toCss(dest); } } pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } pub fn deepClone(this: *const @This(), allocator: std.mem.Allocator) @This() { return css.implementDeepClone(@This(), this, allocator); } }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-distribution) value. pub const ContentDistribution = enum { /// Items are spaced evenly, with the first and last items against the edge of the container. @"space-between", /// Items are spaced evenly, with half-size spaces at the start and end. @"space-around", /// Items are spaced evenly, with full-size spaces at the start and end. @"space-evenly", /// Items are stretched evenly to fill free space. stretch, const css_impl = css.DefineEnumProperty(@This()); pub const eql = css_impl.eql; pub const hash = css_impl.hash; pub const parse = css_impl.parse; pub const toCss = css_impl.toCss; pub const deepClone = css_impl.deepClone; }; /// An [``](https://www.w3.org/TR/css-align-3/#typedef-overflow-position) value. pub const OverflowPosition = enum { /// If the size of the alignment subject overflows the alignment container, /// the alignment subject is instead aligned as if the alignment mode were start. safe, /// Regardless of the relative sizes of the alignment subject and alignment /// container, the given alignment value is honored. unsafe, const css_impl = css.DefineEnumProperty(@This()); pub const eql = css_impl.eql; pub const hash = css_impl.hash; pub const parse = css_impl.parse; pub const toCss = css_impl.toCss; pub const deepClone = css_impl.deepClone; }; /// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-position) value. pub const ContentPosition = enum { /// Content is centered within the container. center, /// Content is aligned to the start of the container. start, /// Content is aligned to the end of the container. end, /// Same as `start` when within a flexbox container. @"flex-start", /// Same as `end` when within a flexbox container. @"flex-end", const css_impl = css.DefineEnumProperty(@This()); pub const eql = css_impl.eql; pub const hash = css_impl.hash; pub const parse = css_impl.parse; pub const toCss = css_impl.toCss; pub const deepClone = css_impl.deepClone; }; pub const SelfPositionInner = struct { /// An overflow alignment mode. overflow: ?OverflowPosition, /// A self position keyword. value: SelfPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } }; pub const ContentPositionInner = struct { /// An overflow alignment mode. overflow: ?OverflowPosition, /// A content position keyword. value: ContentPosition, pub fn eql(lhs: *const @This(), rhs: *const @This()) bool { return css.implementEql(@This(), lhs, rhs); } }; const FlexLinePack = css.css_properties.flex.FlexLinePack; const BoxPack = css.css_properties.flex.BoxPack; const FlexPack = css.css_properties.flex.FlexPack; const BoxAlign = css.css_properties.flex.BoxAlign; const FlexAlign = css.css_properties.flex.FlexAlign; const FlexItemAlign = css.css_properties.flex.FlexItemAlign; pub const AlignHandler = struct { align_content: ?struct { AlignContent, VendorPrefix } = null, flex_line_pack: ?struct { FlexLinePack, VendorPrefix } = null, justify_content: ?struct { JustifyContent, VendorPrefix } = null, box_pack: ?struct { BoxPack, VendorPrefix } = null, flex_pack: ?struct { FlexPack, VendorPrefix } = null, align_self: ?struct { AlignSelf, VendorPrefix } = null, flex_item_align: ?struct { FlexItemAlign, VendorPrefix } = null, justify_self: ?JustifySelf = null, align_items: ?struct { AlignItems, VendorPrefix } = null, box_align: ?struct { BoxAlign, VendorPrefix } = null, flex_align: ?struct { FlexAlign, VendorPrefix } = null, justify_items: ?JustifyItems = null, row_gap: ?GapValue = null, column_gap: ?GapValue = null, has_any: bool = false, pub fn handleProperty(this: *AlignHandler, property: *const Property, dest: *css.DeclarationList, context: *css.PropertyHandlerContext) bool { switch (property.*) { .@"align-content" => |*val| { this.flex_line_pack = null; this.handlePropertyHelper(dest, context, "align_content", &val.*[0], val.*[1]); }, .@"flex-line-pack" => |*val| this.handlePropertyHelper(dest, context, "flex_line_pack", &val.*[0], val.*[1]), .@"justify-content" => |*val| { this.box_pack = null; this.flex_pack = null; this.handlePropertyHelper(dest, context, "justify_content", &val.*[0], val.*[1]); }, .@"box-pack" => |*val| this.handlePropertyHelper(dest, context, "box_pack", &val.*[0], val.*[1]), .@"flex-pack" => |*val| this.handlePropertyHelper(dest, context, "flex_pack", &val.*[0], val.*[1]), .@"place-content" => |*val| { this.flex_line_pack = null; this.box_pack = null; this.flex_pack = null; this.handlePropertyMaybeFlush(dest, context, "align_content", &val.@"align", VendorPrefix.NONE); this.handlePropertyMaybeFlush(dest, context, "justify_content", &val.justify, VendorPrefix.NONE); this.handlePropertyHelper(dest, context, "align_content", &val.@"align", VendorPrefix.NONE); this.handlePropertyHelper(dest, context, "justify_content", &val.justify, VendorPrefix.NONE); }, .@"align-self" => |*val| { this.flex_item_align = null; this.handlePropertyHelper(dest, context, "align_self", &val.*[0], val.*[1]); }, .@"flex-item-align" => |*val| this.handlePropertyHelper(dest, context, "flex_item_align", &val.*[0], val.*[1]), .@"justify-self" => |*val| { this.justify_self = css.generic.deepClone(@TypeOf(val.*), val, context.allocator); this.has_any = true; }, .@"place-self" => |*val| { this.flex_item_align = null; this.handlePropertyHelper(dest, context, "align_self", &val.@"align", VendorPrefix.NONE); this.justify_self = css.generic.deepClone(@TypeOf(val.justify), &val.justify, context.allocator); }, .@"align-items" => |*val| { this.box_align = null; this.flex_align = null; this.handlePropertyHelper(dest, context, "align_items", &val.*[0], val.*[1]); }, .@"box-align" => |*val| this.handlePropertyHelper(dest, context, "box_align", &val.*[0], val.*[1]), .@"flex-align" => |*val| this.handlePropertyHelper(dest, context, "flex_align", &val.*[0], val.*[1]), .@"justify-items" => |*val| { this.justify_items = css.generic.deepClone(@TypeOf(val.*), val, context.allocator); this.has_any = true; }, .@"place-items" => |*val| { this.box_align = null; this.flex_align = null; this.handlePropertyHelper(dest, context, "align_items", &val.@"align", VendorPrefix.NONE); this.justify_items = css.generic.deepClone(@TypeOf(val.justify), &val.justify, context.allocator); }, .@"row-gap" => |*val| { this.row_gap = css.generic.deepClone(@TypeOf(val.*), val, context.allocator); this.has_any = true; }, .@"column-gap" => |*val| { this.column_gap = css.generic.deepClone(@TypeOf(val.*), val, context.allocator); this.has_any = true; }, .gap => |*val| { this.row_gap = css.generic.deepClone(@TypeOf(val.row), &val.row, context.allocator); this.column_gap = css.generic.deepClone(@TypeOf(val.column), &val.column, context.allocator); this.has_any = true; }, .unparsed => |*val| { if (isAlignProperty(val.property_id)) { this.flush(dest, context); bun.handleOom(dest.append(context.allocator, property.*)); } else { return false; } }, else => return false, } return true; } pub fn finalize(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext) void { this.flush(dest, context); } fn flush(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext) void { if (!this.has_any) { return; } this.has_any = false; var align_content = bun.take(&this.align_content); var justify_content = bun.take(&this.justify_content); var align_self = bun.take(&this.align_self); var justify_self = bun.take(&this.justify_self); var align_items = bun.take(&this.align_items); var justify_items = bun.take(&this.justify_items); const row_gap = bun.take(&this.row_gap); const column_gap = bun.take(&this.column_gap); var box_align = bun.take(&this.box_align); var box_pack = bun.take(&this.box_pack); var flex_line_pack = bun.take(&this.flex_line_pack); var flex_pack = bun.take(&this.flex_pack); var flex_align = bun.take(&this.flex_align); var flex_item_align = bun.take(&this.flex_item_align); // 2009 properties this.flushPrefixedProperty(dest, context, "box-align", bun.take(&box_align)); this.flushPrefixedProperty(dest, context, "box-pack", bun.take(&box_pack)); // 2012 properties this.flushPrefixedProperty(dest, context, "flex-pack", bun.take(&flex_pack)); this.flushPrefixedProperty(dest, context, "flex-align", bun.take(&flex_align)); this.flushPrefixedProperty(dest, context, "flex-item-align", bun.take(&flex_item_align)); this.flushPrefixedProperty(dest, context, "flex-line-pack", bun.take(&flex_line_pack)); this.flushLegacyProperty(dest, context, Feature.align_content, &align_content, null, .{ FlexLinePack, "flex-line-pack" }); this.flushLegacyProperty(dest, context, Feature.justify_content, &justify_content, .{ BoxPack, "box-pack" }, .{ FlexPack, "flex-pack" }); if (context.targets.isCompatible(.place_content)) { this.flushShorthandHelper( dest, context, .{ .prop = "place-content", .ty = PlaceContent }, .{ .feature = Feature.align_content, .prop = "align-content" }, &align_content, &justify_content, .{ .feature = Feature.justify_content, .prop = "justify-content" }, ); } this.flushStandardPropertyHelper(dest, context, "align-content", bun.take(&align_content), Feature.align_content); this.flushStandardPropertyHelper(dest, context, "justify-content", bun.take(&justify_content), Feature.justify_content); this.flushLegacyProperty(dest, context, Feature.align_self, &align_self, null, .{ FlexItemAlign, "flex-item-align" }); if (context.targets.isCompatible(.place_self)) { this.flushShorthandHelper(dest, context, .{ .prop = "place-self", .ty = PlaceSelf }, .{ .feature = Feature.align_self, .prop = "align-self" }, &align_self, &justify_self, null); } this.flushStandardPropertyHelper(dest, context, "align-self", bun.take(&align_self), Feature.align_self); this.flushUnprefixProperty(dest, context, "justify-self", bun.take(&justify_self)); this.flushLegacyProperty(dest, context, Feature.align_items, &align_items, .{ BoxAlign, "box-align" }, .{ FlexAlign, "flex-align" }); if (context.targets.isCompatible(css.compat.Feature.place_items)) { this.flushShorthandHelper(dest, context, .{ .prop = "place-items", .ty = PlaceItems }, .{ .feature = Feature.align_items, .prop = "align-items" }, &align_items, &justify_items, null); } this.flushStandardPropertyHelper(dest, context, "align-items", bun.take(&align_items), Feature.align_items); this.flushUnprefixProperty(dest, context, "justify-items", bun.take(&justify_items)); if (row_gap != null and column_gap != null) { dest.append(context.allocator, Property{ .gap = Gap{ .row = row_gap.?, .column = column_gap.?, } }) catch |err| bun.handleOom(err); } else { if (row_gap != null) { bun.handleOom(dest.append(context.allocator, Property{ .@"row-gap" = row_gap.? })); } if (column_gap != null) { bun.handleOom(dest.append(context.allocator, Property{ .@"column-gap" = column_gap.? })); } } } fn handlePropertyMaybeFlush(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: []const u8, val: anytype, vp: VendorPrefix) void { // If two vendor prefixes for the same property have different // values, we need to flush what we have immediately to preserve order. if (@field(this, prop)) |*v| { if (!val.eql(&v[0]) and !bun.bits.contains(VendorPrefix, v[1], vp)) { this.flush(dest, context); } } } fn handlePropertyHelper(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: []const u8, val: anytype, vp: VendorPrefix) void { this.handlePropertyMaybeFlush(dest, context, prop, val, vp); // Otherwise, update the value and add the prefix. if (@field(this, prop)) |*tuple| { tuple.*[0] = css.generic.deepClone(@TypeOf(val.*), val, context.allocator); bun.bits.insert(VendorPrefix, &tuple.*[1], vp); } else { @field(this, prop) = .{ css.generic.deepClone(@TypeOf(val.*), val, context.allocator), vp }; this.has_any = true; } } // Gets prefixes for standard properties. fn flushPrefixesHelper(_: *AlignHandler, context: *css.PropertyHandlerContext, comptime feature: Feature) VendorPrefix { var prefix = context.targets.prefixes(VendorPrefix.NONE, feature); // Firefox only implemented the 2009 spec prefixed. // Microsoft only implemented the 2012 spec prefixed. prefix.moz = false; prefix.ms = false; return prefix; } fn flushStandardPropertyHelper(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: []const u8, key: anytype, comptime feature: Feature) void { if (key) |v| { const val = v[0]; var prefix = v[1]; // If we have an unprefixed property, override necessary prefixes. prefix = if (prefix.none) flushPrefixesHelper(this, context, feature) else prefix; bun.handleOom(dest.append(context.allocator, @unionInit(Property, prop, .{ val, prefix }))); } } fn flushLegacyProperty( this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime feature: Feature, key: anytype, comptime prop_2009: ?struct { type, []const u8 }, comptime prop_2012: ?struct { type, []const u8 }, ) void { _ = this; // autofix if (key.*) |v| { const val = v[0]; var prefix = v[1]; // If we have an unprefixed standard property, generate legacy prefixed versions. prefix = context.targets.prefixes(prefix, feature); if (prefix.none) { if (comptime prop_2009) |p2009| { // 2009 spec, implemented by webkit and firefox. if (context.targets.browsers) |targets| { var prefixes_2009 = VendorPrefix{}; if (Feature.isFlex2009(targets)) { prefixes_2009.webkit = true; } if (prefix.moz) { prefixes_2009.moz = true; } if (!prefixes_2009.isEmpty()) { const s = brk: { const T = comptime p2009[0]; if (comptime T == css.css_properties.flex.BoxOrdinalGroup) break :brk @as(?i32, val); break :brk p2009[0].fromStandard(&val); }; if (s) |a| { dest.append(context.allocator, @unionInit(Property, p2009[1], .{ a, prefixes_2009, })) catch |err| bun.handleOom(err); } } } } } // 2012 spec, implemented by microsoft. if (prefix.ms) { if (comptime prop_2012) |p2012| { const s = brk: { const T = comptime p2012[0]; if (comptime T == css.css_properties.flex.BoxOrdinalGroup) break :brk @as(?i32, val); break :brk p2012[0].fromStandard(&val); }; if (s) |q| { dest.append(context.allocator, @unionInit(Property, p2012[1], .{ q, VendorPrefix.MS, })) catch |err| bun.handleOom(err); } } } // Remove Firefox and IE from standard prefixes. prefix.moz = false; prefix.ms = false; } } fn flushPrefixedProperty(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: []const u8, key: anytype) void { _ = this; // autofix if (key) |v| { const val = v[0]; const prefix = v[1]; bun.handleOom(dest.append(context.allocator, @unionInit(Property, prop, .{ val, prefix }))); } } fn flushUnprefixProperty(this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: []const u8, key: anytype) void { _ = this; // autofix if (key) |v| { const val = v; bun.handleOom(dest.append(context.allocator, @unionInit(Property, prop, val))); } } fn flushShorthandHelper( this: *AlignHandler, dest: *css.DeclarationList, context: *css.PropertyHandlerContext, comptime prop: struct { prop: []const u8, ty: type }, comptime align_prop: struct { feature: Feature, prop: []const u8, }, align_val: anytype, justify_val: anytype, comptime justify_prop: ?struct { feature: Feature, prop: []const u8, }, ) void { // Only use shorthand if both align and justify are present if (align_val.*) |*__v1| { const @"align" = &__v1.*[0]; const align_prefix: *css.VendorPrefix = &__v1.*[1]; if (justify_val.*) |*__v2| { const justify = __v2; const intersection = align_prefix.bitwiseAnd(if (comptime justify_prop != null) __v2.*[1] else align_prefix.*); // Only use shorthand if unprefixed. if (intersection.none) { // Add prefixed longhands if needed. align_prefix.* = flushPrefixesHelper(this, context, align_prop.feature); align_prefix.none = false; if (!align_prefix.isEmpty()) { dest.append( context.allocator, @unionInit(Property, align_prop.prop, .{ css.generic.deepClone(@TypeOf(@"align".*), @"align", context.allocator), align_prefix.* }), ) catch |err| bun.handleOom(err); } if (comptime justify_prop != null) { const justify_actual = &__v2.*[0]; const justify_prefix = &__v2.*[1]; justify_prefix.* = this.flushPrefixesHelper(context, justify_prop.?.feature); justify_prefix.none = false; if (!justify_prefix.isEmpty()) { dest.append( context.allocator, @unionInit(Property, justify_prop.?.prop, .{ css.generic.deepClone(@TypeOf(justify_actual.*), justify_actual, context.allocator), justify_prefix.* }), ) catch |err| bun.handleOom(err); } // Add shorthand. dest.append( context.allocator, @unionInit(Property, prop.prop, prop.ty{ .@"align" = css.generic.deepClone(@TypeOf(@"align".*), @"align", context.allocator), .justify = css.generic.deepClone(@TypeOf(justify_actual.*), justify_actual, context.allocator), }), ) catch |err| bun.handleOom(err); } else { // Add shorthand. dest.append( context.allocator, @unionInit(Property, prop.prop, prop.ty{ .@"align" = css.generic.deepClone(@TypeOf(@"align".*), @"align", context.allocator), .justify = css.generic.deepClone(@TypeOf(justify.*), justify, context.allocator), }), ) catch |err| bun.handleOom(err); } align_val.* = null; justify_val.* = null; } } } } }; fn isAlignProperty(property_id: css.PropertyId) bool { return switch (property_id) { .@"align-content", .@"flex-line-pack", .@"justify-content", .@"box-pack", .@"flex-pack", .@"place-content", .@"align-self", .@"flex-item-align", .@"justify-self", .@"place-self", .@"align-items", .@"box-align", .@"flex-align", .@"justify-items", .@"place-items", .@"row-gap", .@"column-gap", .gap, => true, else => false, }; } const bun = @import("bun"); const std = @import("std"); const Allocator = std.mem.Allocator;