1 /// Holds the [Attribute] type which can be passed to [elemi.elem|elem] to add attributes to
2 /// XML and HTML elements.
3 module elemi.attribute;
4 
5 ///
6 unittest {
7     import elemi.xml;
8 
9     // Create new attributes with `attr`
10     auto a = attr("key") = "value";
11     assert(a == `key="value"`);
12 
13     // And pass them to elements
14     assert(elem!"div"(a) == `<div key="value"></div>`);
15 
16     assert(elem!"div"(
17         attr("key") = "value",
18         attr("class") = "one two three",
19     ) == `<div key="value" class="one two three"></div>`);
20 }
21 
22 import std.string;
23 
24 import elemi.internal;
25 
26 pure @safe:
27 
28 
29 /// Represents an XML/HTML attribute. It can be easily created with the [attr] function.
30 struct Attribute {
31 
32     pure:
33 
34     /// Name of the attribute.
35     string name;
36 
37     /// Value assigned to the attribute.
38     string value;
39 
40     /// Assign a new value to this attribute. Retains the original key.
41     /// Params:
42     ///     newValue = Value assigned to the attribute. i-strings are supported.
43     ///         The value can also be passed as an array of strings, in which case they will be
44     ///         joined with a space. That comes handy for CSS classes.
45     /// Returns:
46     ///     The attribute.
47     Attribute opAssign(string newValue) {
48 
49         value = newValue;
50         return this;
51 
52     }
53 
54     /// ditto
55     Attribute opAssign(string[] newValue) {
56 
57         value = newValue.join(" ");
58         return this;
59 
60     }
61 
62     static if (withInterpolation) {
63         /// ditto
64         Attribute opAssign(Ts...)(InterpolationHeader, Ts values, InterpolationFooter) {
65             import std.conv : text;
66             value = text(values);
67             return this;
68         }
69 
70         ///
71         pure @safe unittest {
72             auto a = Attribute("name");
73             a = i"1+2 is $(1+2)";
74             assert(a == `name="1+2 is 3"`);
75         }
76     }
77 
78     string toString() {
79 
80         return format!q{%s="%s"}(name, value.escapeHTML);
81 
82     }
83 
84     alias toString this;
85 
86 }
87 
88 /// Create an XML/HTML attribute.
89 ///
90 /// Params:
91 ///     name  = Name for the attribute.
92 ///     value = Value for the attribute.
93 Attribute attr(string name) {
94 
95     return Attribute(name);
96 
97 }
98 
99 /// ditto
100 Attribute attr(string name)() {
101 
102     return Attribute(name);
103 
104 }
105 
106 /// ditto
107 Attribute attr(string name, string value) {
108 
109     return Attribute(name, value);
110 
111 }
112 
113 /// ditto
114 Attribute attr(string name)(string value) {
115 
116     return Attribute(name, value);
117 
118 }
119 
120 ///
121 pure unittest {
122     auto a = attr("key") = "value";
123     auto b = attr("key", "value");
124 
125     assert(a == `key="value"`);
126     assert(a == b);
127 }
128 
129 ///
130 pure unittest {
131     import elemi.html;
132 
133     assert(elem!"div"(
134         attr("id") = "name",
135         attr("class") = ["hello", "world"],
136     ) == `<div id="name" class="hello world"></div>`);
137 }
138 
139 ///
140 static if (withInterpolation)
141 pure unittest {
142     import elemi.html;
143 
144     assert(elem!"div"(
145         attr("class") = i"interpolate-$(123)-<unsafe>"
146     ) == `<div class="interpolate-123-&lt;unsafe&gt;"></div>`);
147 }