Commit graph

28 commits

Author SHA1 Message Date
FloatingGhost d1db62ef58 Merge remote-tracking branch 'upstream/main' 2023-08-06 18:29:06 +01:00
FloatingGhost 33642c15d3 update deps 2023-08-06 18:19:16 +01:00
Mitchell Hanberg dc57221bc9
feat!: configure runtime attributes function (#202) 2023-06-12 23:38:16 -04:00
Mitchell Hanberg 07c82e21d3
Dynamic Attributes (#190)
* Move directories for ast tests to match convention

* feat!: Rename `:let` to `:let!`

We use the "bang" style as the reserved keyword to differentiate it from
other possible attributes.

* feat: use Phoenix.HTML as the default engine

I am choosing to leverage this library in order to quickly get dynamic
attributes (see #183) up and running.

This also ensures that folks who wish to use Temple outside of a Phoenix
project with get some nice HTML functions as well as properly escaped
HTML out of the box.

This can be made optional if Temple becomes decoupled from the render
and it including HTML specific packages becomes a strange.

* feat: Allow user to make their own Component module

The component module is essentially to defer compiling functions that the
user might not need. The component, render_slot, and inner_block functions
are only mean to be used when there isn't another implementation.

In the case of a LiveView application, LiveView is providing the
component runtime implementation. This was causing some compile time
warnings for temple, because it was using the LiveView engine at compile
time (for Temple, not the user's application) and LiveView hadn't been
compiled or loaded.

So, now we defer this to the user to make their own module and import it
where necessary.

* feat: Pass dynamic attributes with the :rest! attribute

The :rest! attribute can be used to pass in a dynamic list of attributes
to be mixed into the static ones at runtime.

Since this cannot be properly escaped by any engine, we have to mark it
as safe and then allow the function to escape it for us. I decided to
leverage the `attributes_escape/1` function from `phoenix_html`. There
isn't really any point in making my own version of this or vendoring it.

Now you can also pass a variable as the attributes as well if you only
want to pass through attributes from a calling component.

The :rest! attribute also works with components, allowing you to pass
a dynamic list of args into them.

Fixes #183

* Move test components to their own file.

* docs(components): Update documentation on Temple.Components

* docs(guides): Mention attributes_escape/1 function in the guides

* chore(test): Move helper to it's own module

* feat: rest! support for slots

* docs(guides): Dynamic attributes

* ci: downgrade runs-on to support OTP 23
2023-01-21 06:44:29 -05:00
Mitchell Hanberg f942817994
Utilize the EEx Engine instead of creating an EEx string (#177) 2022-04-19 23:56:46 -04:00
Mitchell Hanberg c965048f40
Better whitespace handling and control (#145)
* Fine tune whitespace

The EEx outut now emits more human-readable and predictable formatting.
This includes proper indenting, at least for each "root" template.

* Internal whitespace control

You can now use a bang version of any nonvoid tag to emit the markup
witout the internal whitespace. This means that there will not be a
newline emitted after the opening tag and before the closing tag.
2021-08-29 17:45:07 -04:00
Mitchell Hanberg d9f00e5147
Class object syntax (#140)
* Class object syntax

Allows for conditionally setting classes on an element.

* Docs for class bindings
2021-06-27 12:04:19 -04:00
Mitchell Hanberg 9d05f74cdf
Properly emit boolean attributes (#139)
* Update some docs

* Properly emit boolean attributes.

* Account for quoted literals when compiling attributes

* Update changelog
2021-06-26 21:47:21 -04:00
Mitchell Hanberg 540692f7cd Void elements can be compiled with no attrs 2021-06-03 23:13:06 -04:00
Mitchell Hanberg fe3aed5df7 Some cleanup 2021-04-09 00:16:30 -04:00
Mitchell Hanberg 41f9b94d0f Hook the AST generation in to the temple macros
- Removes the old way
- Removes the ability to compact an element
2021-04-09 00:16:30 -04:00
Mitchell Hanberg ced2f6ab66 feat: New Component API 2021-01-02 13:22:03 -05:00
Mitchell Hanberg 396978b36c Update test 2020-11-04 20:21:32 -05:00
Mitchell Hanberg 5c5edfa67f case expressions 2020-11-04 19:58:35 -05:00
Mitchell Hanberg 459084285f Fix formatting 2020-08-09 10:10:53 -04:00
Mitchell Hanberg 265c413960 Allow element attrs to be evaluated at runtime
Before this change, only keyword list literals could be passed to
elements. If they had non-literals as values, then those would compile
to EEx expressions.

This allows a non-literal to be passed as attrs and have the entire thing
compile to an EEx expression, which will pass the non-literal to a
"runtime_attrs" function, which evaluates a keyword list into a safe
string.

That last part might need to be reworked if the user is not using
the Phoenix.HTML.Engine EEx Engine.
2020-08-09 10:07:27 -04:00
Mitchell Hanberg 7bf649c4b5 Correctly parse do blocks
Did not correctly parse expressions with do blocks
where the expression had two or more arguments before
the block.
2020-07-22 21:34:50 -04:00
Mitchell Hanberg 1f599f5f6d Handle expressions with do blocks that aren't if/unless/for 2020-07-15 23:23:12 -04:00
Mitchell Hanberg 1a5837d1b7 Components API
Components work very similarly to how they worked before, but with a few
differences.

To define a component, you can create a file in your configured temple
components directory, which defaults to `lib/components`. You would
probably want ot change that to be `lib/my_app_web/components` if you
are building a phoenix app.

This file should be of the `.exs` extension, and contain any temple
compatible code.

You can then use this component in any other temple template.

For example, if I were to define a `flex` component, I would create a
file called `lib/my_app_web/components/flex.exs`, with the following
contents.

```elixir
div class: "flex #{@temple[:class]}", id: @id do
  @children
end
```

And we could use the component like so

```elixir
flex class: "justify-between items-center", id: "arnold" do
  div do: "Hi"
  div do: "I'm"
  div do: "Arnold"
  div do: "Schwarzenegger"
end
```

We've demonstated several features to components in this example.

We can pass assigns to our component, and access them just like we would
in a normal phoenix template. If they don't match up with any assigns we
passed to our component, they will be rendered as-is, and will become a
normal Phoenix assign.

You can also access a special `@temple` assign. This allows you do
optionally pass an assign, and not have the `@my_assign` pass through.
If you didn't pass it to your component, it will evaluate to nil.

The block passed to your component can be accessed as `@children`. This
allows your components to wrap a body of markup from the call site.

In order for components to trigger a recompile when they are changed,
you can call `use Temple.Recompiler` in your `lib/my_app_web.ex` file,
in the `view`, `live_view`, and `live_component` functions

```elixir
def view do
  quote do
    # ...
    use Temple.Recompiler
    # ...
  end
end
```
2020-07-15 22:32:27 -04:00
Mitchell Hanberg 33c95186fb
Compile to EEx (#80)
Code is gross
2020-06-16 15:28:21 -04:00
Mitchell Hanberg 3993c798c0 Join markup with a new line
Text nodes separated by new lines still show whitespace when rendered,
so we should maintain user specified new lines.

Closes #59
Closes #60
2020-04-14 10:40:05 -04:00
Mitchell Hanberg 1093a4d602 Rename props to assigns
This helps stay consistent with the Phoenix nomenclature.
2020-04-14 10:40:01 -04:00
zimt28 fa41e73bb0 Add @props access to components (#66)
* Add @props access to components

* Document `@props` assign
2020-04-14 10:40:00 -04:00
Mitchell Hanberg eb0fde6e83
Don't recursively call generated component macros (#12)
Recursively calling the macros works fine if you `import` the whole
module wherever you are using your components, but not if you `require`
the module.

This is because importing brings in the all the macros into the callers
namespace, which allows them to be called just by the macro name. When
you `require` the module, it will look for the generated 2-arity macro
in the callers namespace, which probably doesn't exist.

We get around this by not recursively calling them and avoiding the
problem all togther. A few utility functions solves the original issue
of wanting to DRY the file.
2019-08-10 01:09:24 -04:00
Mitchell Hanberg 7ad4b0e941 Rename Temple.htm to Temple.temple 2019-07-08 22:29:41 -04:00
Mitchell Hanberg d210d3bff5 Extract safe result from hidden fields within inputs_for/4
Also switches to using `with` instead of `lexical_scoping` because it is
more idiomatic.
2019-07-07 22:26:32 -04:00
Mitchell Hanberg 8daf85fdb3 Allow defcomponent to work with runtime values for assigns
Also allows tags and defcomponents to accept maps in addition to keyword
lists
2019-07-04 00:16:29 -04:00
Mitchell Hanberg 9278f7fb4e Rename to Temple 2019-07-01 22:48:51 -04:00
Renamed from test/dsl_test.exs (Browse further)