chasefleming / elem-go Goto Github PK
View Code? Open in Web Editor NEWType-safe Go library for creating and manipulating HTML elements (with htmx helpers).
License: MIT License
Type-safe Go library for creating and manipulating HTML elements (with htmx helpers).
License: MIT License
Currently, the map of void elements is re-allocated with every call to Render
. This can be optimized by allocating it once and referencing it when needed.
Render
function to avoid repeated allocations.bool
to struct{}
to create a more space-efficient set.To enhance the multimedia capabilities of elem-go
, we propose the introduction of <audio>
, <video>
, and <source>
HTML elements. This will enable users to embed audio and video content more effectively in their applications.
<audio>
, <video>
, and <source>
Elements: Implement utility functions for these elements, consistent with elem-go
's existing architecture.attrs
Subpackage: Add new attributes specific to <audio>
, <video>
, and <source>
to the attrs
subpackage.autoplay
, controls
, loop
, muted
).elem.go
(elem.go#L35).There are several boolean attributes missing from our current implementation. We need to add IsMap
, NoValidate
, and Selected
attributes to our codebase.
attrs.go
in the attrs
packageelem.go
in the elem
packageIsMap
, NoValidate
, and Selected
constants to attrs.go
.booleanAttrs
map in elem.go
.These attributes are boolean attributes that need to be represented in our data structures.
There are cases when else
clause in conditional rendering is not needed, but there is no way to omit the else
argument. This results in somewhat awkward code:
shouldWeShowLink := true
link := elem.A(attrs.Props{attrs.Href: "/foobar"}, elem.Text("link"))
content := elem.Div(nil,
elem.P("Some text"),
elem.If(shouldWeShowLink, link, "")
)
Add new method
Introduce new method like If
but with only then
argument.
Pros: readable, simple
Cons: It's difficult to come up with a name though.
Introduce method chaining
So we could write something like:
If(cond).Then(elem).Else(elem)
If(cond).Then(elem)
Pros: readable
Cons: not clear how to handle errors when then
part is missing; could be a breaking change unless new package or method name is used
Several constants in the attrs
package of elem-go
do not adhere to Go's standard naming conventions. Let's deprecate these constants and introduce new ones with correct naming. This approach will provide a transition period for users to update their code while maintaining backward compatibility.
AllowFullScreen
should be changed to AllowFullscreen
.ReferrerPolicy
should be changed to Referrerpolicy
.NoValidate
should be updated to Novalidate
.MaxLength
should be updated to Maxlength
.DateTime
should be updated to Datetime
.CrossOrigin
should be updated to Crossorigin
.SrcDoc
should be updated to Srcdoc
.IsMap
should be updated to Ismap
I suggest adding a contributing.md file to the project. You can assign this task to me, and I will create the file
At present, the children of elements are defined as interface{}
, which can result in runtime errors. This type choice also causes miscellaneous issues when functions return *Element
and not interface{}
as seen in:
func renderItems(items []Item) []*elem.Element {
return elem.TransformEach(todos, func(todo Todo) *elem.Element {
//...[code]...
}
}
To prioritize compile-time errors over runtime ones and to reduce ambiguity and unnecessary flexibility in the code, let's introduce a Node
interface. Both the return type of functions and the type of element children should be aligned to this Node
type.
In the function renderTodoItems(todos []Todo) []*elem.Element
, the TransformEach
method returns a slice of *Element
. Due to the children of these elements being of the interface{}
type, it introduces ambiguity. This becomes especially problematic when handling diverse node types, like text nodes or other elements.
Adopt the following structure to ensure only valid nodes can be used as children:
func renderItems(items []Item) []elem.Node {
return elem.TransformEach(todos, func(todo Todo) elem.Node {
//...[code]
}
}
Update readme to include #49
It would be useful if elem-go
could include a function, perhaps named Raw
or similar, that allows users to insert raw HTML strings into the document structure. This function would ideally take a raw HTML string as an input and return an elem.Node
that represents this HTML.
content := "<p>Some <strong>html</strong> content</p>"
page := elem.Html(nil,
elem.Head(nil,
elem.Title(nil, elem.Text("Example Page")),
),
elem.Body(nil,
elem.H1(...),
elem.Raw(content),
),
)
htmlOutput := page.Render()
<i>
element, which is commonly used for embedding icons, especially with libraries like FontAwesome.<i>
element in the list of supported elements, with emphasis on its use for icon embedding.The <i>
element, when used with icon libraries like FontAwesome, can represent various icons, such as:
<i class="fa-regular fa-face-smile"></i>
Currently, the NewElement
function in the elem-go
library is public. This exposes internal implementation details that users of the library do not need to interact with directly. To improve the encapsulation and maintain a clean public API, the NewElement
function be made private.
NewElement
function to newElement
.NewElement
within the library to reference newElement
.newElement
private hides internal details from the library users, promoting better encapsulation.github.com/anycable/anycable-go looks like a nice match with go-elem. It's designed for Turbo, but it will work with go-elem and htmx and SSE / Web Sockets.
It can also embed the NATS Server for you.
https://dev.to/chasefleming/building-a-counter-app-with-htmx-go-fiber-and-elem-go-9jd looks good too @chasefleming
We need to add support for commonly used form elements to the library.
The library currently lacks a few HTML form-related elements. These are essential for many web applications that require user inputs or actions. To make our library more comprehensive, it would be great to include the following elements:
<form>
: Form container.<input>
: Input field.<textarea>
: Multi-line text input.<button>
: Clickable button.<select>
: Dropdown list.<option>
: Option in a dropdown list.<label>
: Label for an input element.The styles
package needs support additional font and text properties. Add them to here under "Font and Text Properties:"
Line 84 in 2b7e1d6
font-variant
font-stretch
word-wrap
I have a need to take the HTML to PDF and embed a QR code that links back to the page.
The use case is an Art portfolio site for a friend. She needs the HTML to show each art piece with some text etc, and to have a PDF of that page with a QR code on it.
the idea is that she can hand them out at her fairs if someone likes a piece, and the can later just scan the QR code and be taken to the web page.
https://github.com/benoitkugler/go-weasyprint looks like it will do it in native go.
The traditional way is a chrome embedded render I think which I would like to avoid.
Node
should now be returned instead of *Element
. Update the Show
utility: https://github.com/chasefleming/elem-go/blob/main/utils.go#L34
The current implementation of Element.Render
uses string concatenation which can be inefficient, especially for larger projects. To enhance performance, refactor the method to use strings.Builder
.
Element.Render
to use strings.Builder
for string operations.RenderTo(builder *strings.Builder)
which writes directly to the passed builder.RenderTo
as well.ARIA attributes for accessibility need to be added to the attributes subpackage.
aria-activedescendant
aria-atomic
aria-autocomplete
aria-busy
aria-checked
aria-controls
aria-describedby
aria-disabled
aria-expanded
aria-flowto
aria-haspopup
aria-hidden
aria-invalid
aria-label
aria-labelledby
aria-level
aria-live
aria-modal
aria-multiline
aria-multiselectable
aria-orientation
aria-owns
aria-placeholder
aria-pressed
aria-readonly
aria-required
aria-roledescription
aria-selected
aria-sort
aria-valuemax
aria-valuemin
aria-valuenow
aria-valuetext
Add missing properties related to the Grid layout model.
<iframe>
element under a new section called "Embedded Content" in elements.go
attrs
package add the following attributes for iframe
support:
loading
allow
allowfullscreen
csp
referrerpolicy
sandbox
srcdoc
<iframe>
element's rendering with various attributes to ensure correct behavior.I could very well be blind.
I do not see a way to insert a "link" in the head
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css">
What am I missing?
Does anyone have any preference for the top and bottom layers ?
Bottom being the Data Access Layer. Examples being: Sqlc, Ent, Pocketable, etc
Top being the reusable GUI Components. Examples being Material Design Web, DaisyUI, etc.
The current implementation of the String
method in the Style
type uses string concatenation which can lead to multiple memory allocations, especially for styles with a large number of properties. For better performance, switch to strings.Builder
.
See this line for context: https://github.com/chasefleming/elem-go/blob/main/utils.go#L19
HTML Input supports the step
attribute, but it doesn't appear that this is currently supported by this package.
The styles
package needs support various outline properties. Add them to here under "Box Model:" https://github.com/chasefleming/elem-go/blob/2b7e1d671ae12e5f272770d51c8801b28d970a94/styles/styles.go#L44C3-L44C3
outline
outline-style
outline-color
outline-width
outline-offset
The library currently lacks a dedicated function to create <style>
tags for embedding CSS directly into HTML documents. We could add the addition of a Style
function and a CSS
function.
The Style
function should return an *Element
struct representing the <style>
tag, allowing users to insert raw CSS into an HTML document.
cssContent := `
body { background-color: #f0f0f0; }
h1 { color: #333; }
`
styleTag := elem.Style(nil, styles.CSS(cssContent))
document := elem.Html(nil,
elem.Head(nil,
styleTag,
),
// ... other body elements
)
There are several text formatting and structural elements missing from our elem
package. These need to be added.
Blockquote
: Blockquote <blockquote>
Br
: Break <br>
Code
: Code <code>
Em
: Emphasis <em>
Hr
: Horizontal Rule <hr>
Pre
: Preformatted Text <pre>
Strong
: Strong <strong>
To facilitate the display of tables, we need to introduce table-related elements.
Add support for the following table elements:
<table>
: Represents tabular data.<thead>
: Groups the header content in a table.<tbody>
: Groups the body content in a table.<tfoot>
: Groups the footer content in a table.<tr>
: Represents a row of cells in a table.<th>
: Represents a header cell in a table.<td>
: Represents a data cell in a table.Tests should also be added for each
It would be beneficial to have an example of a "Todo List" within the repository. Todo lists are commonly used features in many applications, and having a concrete example would greatly benefit those who are looking for practical applications of the repo's features.
examples/
directory.Render
now adds the preamble as noted in #91 , <!DOCTYPE html>
by default. While this is the most common scenario, there may still be other environments where this is not desired. We should still offer an option to disable this if necessary. In order to keep Render()
simple and to not break backwards compatibility we should add a new method called RenderWithOptions
that takes a custom param of RenderOptions
with a flag to DisablePreamble
if desired. We could also use this to later allow less common preambles to be applied.
options := RenderOptions{DisablePreamble: true}
htmlString := myHtmlElement.RenderWithOptions(options)
This issue proposes the addition of a None
node to the elem-go
library to facilitate cases where no output is required in conditional rendering scenarios.
Currently, when using the If
function for conditional rendering, both the true
and false
branches require an element. There are situations where we might not want to render anything for the false
branch, but that is not possible without adding elem.Text("")
.
Implement a NoneNode
type that implements the Node
interface but renders nothing. This node would be used as a placeholder in conditional rendering when no output is desired.
content := elem.If[elem.Node](true, elem.Div(nil, elem.Text("It is true!"), None())
// Outputs: <div>It is true!</div>
emptyDiv := elem.Div(nil, None())
// Outputs: <div></div>
If
function.Add missing properties related to the box model in the following file: https://github.com/chasefleming/elem-go/blob/main/styles/styles.go
min-width
min-height
border-top
, border-right
, border-bottom
, border-left
border-top-color
, border-right-color
, border-bottom-color
, border-left-color
border-top-width
, border-right-width
, border-bottom-width
, border-left-width
border-top-style
, border-right-style
, border-bottom-style
, border-left-style
The Merge
method would streamline the development process by allowing developers to combine multiple style objects more succinctly. An example use case would be to allow developers to combine base styles with conditional styles that may depend on the state or other factors.
baseButtonStyle := styles.Props{
styles.Padding: "10px 15px",
styles.Border: "none",
styles.FontWeight: "bold",
}
primaryStyles := styles.Props{
styles.BackgroundColor: "blue",
styles.Color: "white",
}
secondaryStyles := styles.Props{
styles.BackgroundColor: "red",
styles.Color: "white",
}
primaryButtonStyles := styles.Merge(baseButtonStyle, primaryStyles)
secondaryButtonStyles := styles.Merge(baseButtonStyle, secondaryStyles)
In the proposed Merge
function, the later style objects should take precedence over earlier ones in cases where the same property is defined in multiple style objects.
Yes, you can render comments as Text
nodes:
content := Text("<!-- this is a comment -->").Render()
But it's too verbose and error prone (could easily mix up opening/closing comment quotes)
Add Comment
element:
content := Comment("this is a comment").Render()
Pros: intuitive
Cons:
As far as I know, many programming language parsers consider comments as properties of "real" nodes, not as separate nodes. But that seems too complex both to use and implement.
Add missing properties related to the Flexbox layout model in the following file: https://github.com/chasefleming/elem-go/blob/main/styles/styles.go
I am working with DaisyUI.
There is a requirement to set a color scheme like this:
<html data-theme="cupcake"></html>
I can't figure out how to do that with this library. Am I missing something?
PS: Please consider opening up the GitHub discussions on this. I always think that asking a question should be under discussions and reporting a real issue under issues.
The styles
package needs support various font and text properties. Add them to here under "Font & Text Properties:"
Line 84 in 2b7e1d6
font-style
text-shadow
vertical-align
word-spacing
word-break
text-indent
The new boolean attributes feature from #49 will help simplify the code in the todo app code. Refactor it.
When using elem.Html
, everything gets neatly wrapped in an <html></html>
element as expected.
But it's expected/required by browsers to have a <!DOCTYPE html>
preamble. It'll work without it, but you immediately get a warning in the console as without the preamble you're rendering in the legacy/quirks mode which is usually not desirable. Since a document can only have a single html
element, it seems like elem-go could safely emit it.
So two questions:
RenderTo()
which feels a bit iffy. Is there a better way?There are several common style values, especially within the realm of CSS, where percentages are frequently used (e.g., width, height, margin, padding). Additionally, other measurements like pixels (px
), em units (em
), and viewport heights (vh
) or widths (vw
) are commonly used in style attributes.
We could simplify the usage of these common values:
widthStyle := styles.Props{
styles.Width: styles.Percent(50), // Returns "50%"
}
paddingStyle := styles.Props{
styles.Padding: styles.Pixels(10), // Returns "10px"
}
vh
and vw
values.divStyle := styles.Props
styles.Height: styles.ViewportHeight(100), // Returns "100vh"
styles.Width: styles.ViewportWidth(50), // Returns "50vw"
}
em
and rem
values which can be based on float inputs, given that they often have fractional values.fontSizeStyle := styles.Props{
styles.FontSize: styles.Rem(1.5), // Returns "1.50rem"
}
By providing these utility functions, you simplify the process of defining common style values for users, reduce potential errors, and also ensure a consistent approach across the codebase. It makes the code cleaner and more expressive.
The current Go package for htmx
is missing several attributes that are present in the htmx library. These attributes are crucial for leveraging the full power and flexibility of htmx.
Request Headers and Content-Type:
hx-headers
: Specifies additional headers for the request.hx-content
: Specifies how the content should be encoded.Request Timeout and Retries:
hx-timeout
: Specifies a timeout, in milliseconds, for the request.hx-retry
: Provides a mechanism to retry requests.hx-retry-timeout
: Specifies how long, in milliseconds, to wait in between retries.Events:
hx-on
: Specifies JavaScript to be executed in response to htmx events.History:
hx-history-elt
: Specifies an alternate element to use for history purposes.hx-history-attr
: Specifies the attribute to use when pulling values for history purposes.Response Processing:
hx-select
: Selects content from the response for swapping.hx-ext
: Specifies extensions to activate for the element.hx-vals
: Specifies values for the request.Caching:
hx-cache
: Provides control over AJAX request caching.Miscellaneous:
hx-triggering-element
: Gets set to the element that triggered the current request.hx-triggering-event
: Gets set to the event that triggered the current request.Some attributes inherently signify a true
value merely by their presence, and attempting to set them to "false"
may not yield the expected result. For instance, consider the following code snippet, which will still display as checked:
checkbox := Input(Attrs{
"type": "checkbox",
"checked": "false",
})
The challenge arises when a developer wants to explicitly set an attribute to false
to avoid creating multiple attribute sets with conditionals. To address this, we need to modify the behavior so that when an attribute's value is `"false", it is omitted from the rendered output. Furthermore, if an attribute has an empty string value, it should be rendered without any value.
checkbox
should render as <input type="checkbox" checked>
if "true"
checkbox
should render as <input type="checkbox">
if "false"
or and empty string.elem.go
code."true"
, "false"
, and an empty string."false"
will be omitted in the rendered output.The library currently supports ul
and li
elements. But, there are some other standard HTML list-related elements that should be included.
To make the library more holistic and provide developers with a broader range of list options, we should add the following elements:
<ol>
: Ordered list.<dl>
: Description list.<dt>
: Description term. Represents the term in a key-value pair in a <dl>
.<dd>
: Description description. Represents the value in a key-value pair in a <dl>
.Implement the <optgroup>
element to enhance the <select>
dropdown functionality. This element allows for the grouping of <option>
elements, improving organization and user experience in forms with numerous options.
<optgroup>
element under the "Form Elements" section in elements.go
.<optgroup>
can be nested within <select>
and can contain multiple <option>
elements.Develop a test to verify an output like this:
<select name="cars" id="cars">
<optgroup label="Swedish Cars">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
</optgroup>
<optgroup label="German Cars">
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</optgroup>
</select>
The current utility function Show(condition bool, ifTrue, ifFalse Node) Node
is specifically tailored for conditional rendering of nodes. This results in a lack of clarity in the function's intent and its applicability, making it less versatile than desired. A use case where the function falls short is in conditionally setting attributes.
The introduction of a more semantically clear and generic function called If
. This function would serve for conditional node rendering but also to flexibly handle attributes and other scenarios.
isAdmin := true
adminLink := elem.A(elem.Attrs{attrs.Href: "/admin"}, elem.Text("Admin Panel"))
guestLink := elem.A(elem.Attrs{attrs.Href: "/login"}, elem.Text("Login"))
content := elem.Div(nil,
elem.H1(nil, elem.Text("Dashboard")),
If[elem.Node](isAdmin, adminLink, guestLink),
)
isEditable := false
inputAttributes := elem.Attrs{
attrs.Type: "text",
attrs.Spellcheck: If[string](isEditable, "true", "false"),
}
content := elem.Input(inputAttributes)
Support needs to be added for the following semantic text content elements:
<figure>
: Represents content, such as illustrations, diagrams, photos, etc.<figcaption>
: Represents a caption or legend for a <figure>
.<mark>
: Denotes highlighted or emphasized text.<time>
: Denotes a specific period in time or a duration.<details>
: Represents a disclosure widget for hiding and showing details.<summary>
: Represents a summary, heading, or legend for the content within <details>
.<address>
: Denotes the contact information for its nearest <article>
or <body>
ancestor.The styles
package needs support table attributes. Add them to here in a new category called "Table Attributes": https://github.com/chasefleming/elem-go/blob/main/attrs/attrs.go
colspan
rowspan
headers
scope
We are experiencing naming conflicts and organizational issues with the current implementation of elem.Attrs
and elem.Style
(as found here). To address this, we plan to restructure these parts of the code to achieve better organization and resolve these conflicts.
elem.Attrs
to attrs.Props
to avoid naming conflicts and better represent element properties in our HTML construction.elem.Style
to styles.Props
to differentiate style properties from other HTML attributes more clearly.ApplyStyle
method in favor of a new method that better describes its function.ToInline
method on the Style type, providing a clear mechanism for converting style objects to inline CSS strings.elem.Attrs
to attrs.Props
throughout the codebase to prevent naming conflicts and improve readability.elem.Style
to styles.Props
to segregate styles from other attributes and resolve any conflicts.ApplyStyle
to transition to a clearer and more descriptive method.ToInline
as a new method on Style to streamline the conversion of style properties to inline CSS.ApplyStyle
to the new ToInline
method.Add functions to create new HTML elements that cover form, interactive, and script-supporting functionalities.
Fieldset()
Legend()
Datalist()
Meter()
Output()
Progress()
Dialog()
Menu()
NoScript()
*Element
struct that renders to valid html.The styles
package needs support additional visual properties. Add them to here under "Visual Properties:"
Line 98 in 2b7e1d6
background-position
background-attachment
background-blend-mode
backface-visibility
perspective
transform-origin
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.