Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/solid/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VOLT_APP_NAME=Volt Solid Example
9 changes: 9 additions & 0 deletions examples/solid/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
plugins: [Volt.Formatter],
import_deps: [:phoenix],
inputs: [
"*.{heex,ex,exs}",
"{config,lib,test}/**/*.{heex,ex,exs}",
"assets/**/*.{js,ts,jsx,tsx}"
]
]
6 changes: 6 additions & 0 deletions examples/solid/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/_build/
/cover/
/deps/
/node_modules/
/priv/static/assets/
/npm.lock
10 changes: 10 additions & 0 deletions examples/solid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Volt Solid Example

A minimal Phoenix app using Volt with Solid.

```sh
mix setup
mix phx.server
```

Open <http://localhost:4000>.
3 changes: 3 additions & 0 deletions examples/solid/assets/css/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "tailwindcss" source(none);
@source "../js";
@source "../../lib";
3 changes: 3 additions & 0 deletions examples/solid/assets/images/volt.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions examples/solid/assets/js/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { JSX } from 'solid-js'

export default function Card(props: { title: string; children: JSX.Element }) {
return (
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<h2 class="mb-3 text-xs font-bold uppercase tracking-widest text-slate-400">{props.title}</h2>
{props.children}
</div>
)
}
19 changes: 19 additions & 0 deletions examples/solid/assets/js/Counter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createMemo, createSignal } from 'solid-js'

export default function Counter() {
const [count, setCount] = createSignal(0)
const doubled = createMemo(() => count() * 2)

return (
<div>
<button
type="button"
class="rounded-full bg-cyan-600 px-5 py-2.5 font-semibold text-white shadow-md shadow-cyan-600/25 transition hover:-translate-y-0.5 hover:bg-cyan-700"
onClick={() => setCount((value) => value + 1)}
>
Count is {count()}
</button>
<p class="mt-3 text-sm text-slate-500">Doubled: {doubled()}</p>
</div>
)
}
53 changes: 53 additions & 0 deletions examples/solid/assets/js/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { For } from 'solid-js'
import { render } from 'solid-js/web'
import config from './config.json'
import logo from '../images/volt.svg'
import Counter from './Counter'
import Card from './Card'

const pages = import.meta.glob('./pages/*.ts', { eager: true }) as Record<
string,
{ title: string; description: string }
>

function App() {
return (
<main class="mx-auto mt-12 max-w-2xl space-y-8 px-6 pb-12">
<header class="flex items-center gap-4">
<img src={logo} alt="" class="h-10 w-10 text-cyan-500" />
<div>
<h1 class="text-4xl font-black tracking-tight text-slate-950">{config.name} + Solid</h1>
<p class="text-sm text-slate-500">v{config.version}</p>
</div>
</header>

<Card title="Counter">
<Counter />
</Card>

<Card title="Pages (glob import)">
<ul class="space-y-2">
<For each={Object.entries(pages)}>
{([_path, page]) => (
<li class="flex items-baseline gap-2">
<span class="font-semibold text-slate-800">{page.title}</span>
<span class="text-sm text-slate-500">{page.description}</span>
</li>
)}
</For>
</ul>
</Card>

<footer class="text-center text-xs text-slate-400">Mode: {import.meta.env.MODE}</footer>
</main>
)
}

const dispose = render(() => <App />, document.getElementById('app')!)

if (import.meta.hot) {
import.meta.hot.dispose(() => {
dispose()
})
import.meta.hot.accept()
}
4 changes: 4 additions & 0 deletions examples/solid/assets/js/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "Volt",
"version": "1.0.0"
}
2 changes: 2 additions & 0 deletions examples/solid/assets/js/pages/about.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const title = 'About'
export const description = 'Built with Volt — Elixir-native frontend tooling'
2 changes: 2 additions & 0 deletions examples/solid/assets/js/pages/home.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const title = 'Home'
export const description = 'Welcome to the Volt example app'
14 changes: 14 additions & 0 deletions examples/solid/assets/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"allowJs": true,
"noEmit": true
},
"include": ["js/**/*"]
}
43 changes: 43 additions & 0 deletions examples/solid/config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Config

config :solid_example,
generators: [timestamp_type: :utc_datetime]

config :solid_example, SolidExampleWeb.Endpoint,
url: [host: "localhost"],
adapter: Bandit.PhoenixAdapter,
render_errors: [formats: [html: SolidExampleWeb.ErrorHTML], layout: false],
pubsub_server: SolidExample.PubSub,
live_view: [signing_salt: "volt-example"]

config :logger, :default_formatter,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]

config :phoenix, :json_library, Jason

config :volt,
entry: "assets/js/app.tsx",
outdir: "priv/static/assets",
root: "assets",
sources: ["**/*.{js,ts,jsx,tsx}"],
target: :es2022,
minify: false,
hash: false,
resolve_dirs: ["node_modules", "deps"],
plugins: [Volt.Plugin.Solid],
tailwind: [
css: "assets/css/app.css",
sources: [
%{base: "lib/", pattern: "**/*.{ex,heex}"},
%{base: "assets/", pattern: "**/*.{js,ts,jsx,tsx}"}
]
]

config :volt, :format,
semi: false,
single_quote: true

config :volt, :lint, plugins: [:typescript]

import_config "#{config_env()}.exs"
14 changes: 14 additions & 0 deletions examples/solid/config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Config

config :solid_example, SolidExampleWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: String.to_integer(System.get_env("PORT") || "4000")],
check_origin: false,
code_reloader: true,
debug_errors: true,
secret_key_base:
"volt-example-secret-key-base-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
watchers: []

config :volt, :server,
prefix: "/assets",
watch_dirs: ["lib/", "assets/"]
7 changes: 7 additions & 0 deletions examples/solid/config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Config

config :solid_example, SolidExampleWeb.Endpoint,
cache_static_manifest: "priv/static/cache_manifest.json",
server: true

config :logger, level: :info
8 changes: 8 additions & 0 deletions examples/solid/config/runtime.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Config

if config_env() == :prod do
config :solid_example, SolidExampleWeb.Endpoint,
url: [host: System.get_env("PHX_HOST") || "example.com", port: 443, scheme: "https"],
http: [port: String.to_integer(System.get_env("PORT") || "4000")],
secret_key_base: System.fetch_env!("SECRET_KEY_BASE")
end
9 changes: 9 additions & 0 deletions examples/solid/config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Config

config :solid_example, SolidExampleWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
secret_key_base:
"volt-example-secret-key-base-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
server: false

config :logger, level: :warning
3 changes: 3 additions & 0 deletions examples/solid/lib/solid_example.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule SolidExample do
@moduledoc false
end
22 changes: 22 additions & 0 deletions examples/solid/lib/solid_example/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule SolidExample.Application do
@moduledoc false
use Application

@impl true
def start(_type, _args) do
children = [
SolidExampleWeb.Telemetry,
{DNSCluster, query: Application.get_env(:solid_example, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: SolidExample.PubSub},
SolidExampleWeb.Endpoint
]

Supervisor.start_link(children, strategy: :one_for_one, name: SolidExample.Supervisor)
end

@impl true
def config_change(changed, _new, removed) do
SolidExampleWeb.Endpoint.config_change(changed, removed)
:ok
end
end
54 changes: 54 additions & 0 deletions examples/solid/lib/solid_example_web.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
defmodule SolidExampleWeb do
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)

def router do
quote do
use Phoenix.Router, helpers: false
import Plug.Conn
import Phoenix.Controller
import Phoenix.LiveView.Router
end
end

def controller do
quote do
use Phoenix.Controller, formats: [:html, :json]
import Plug.Conn
unquote(verified_routes())
end
end

def live_view do
quote do
use Phoenix.LiveView
unquote(html_helpers())
end
end

def html do
quote do
use Phoenix.Component
import Phoenix.Controller, only: [get_csrf_token: 0]
unquote(html_helpers())
end
end

defp html_helpers do
quote do
import Phoenix.HTML
alias SolidExampleWeb.Layouts
unquote(verified_routes())
end
end

def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: SolidExampleWeb.Endpoint,
router: SolidExampleWeb.Router,
statics: SolidExampleWeb.static_paths()
end
end

defmacro __using__(which) when is_atom(which), do: apply(__MODULE__, which, [])
end
17 changes: 17 additions & 0 deletions examples/solid/lib/solid_example_web/components/layouts.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
defmodule SolidExampleWeb.Layouts do
use SolidExampleWeb, :html

embed_templates "layouts/*"

attr :flash, :map, required: true
attr :current_scope, :map, default: nil
slot :inner_block, required: true

def app(assigns) do
~H"""
<main class="min-h-screen bg-slate-50 px-4 py-1 text-slate-900">
{render_slot(@inner_block)}
</main>
"""
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
<.live_title default="Volt Solid Example">Volt Solid Example</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<script defer phx-track-static type="module" src={Volt.entry_path(SolidExampleWeb.Endpoint)}></script>
</head>
<body>
{@inner_content}
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule SolidExampleWeb.ErrorHTML do
use SolidExampleWeb, :html

def render(template, _assigns), do: Phoenix.Controller.status_message_from_template(template)
end
41 changes: 41 additions & 0 deletions examples/solid/lib/solid_example_web/endpoint.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule SolidExampleWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :solid_example

@session_options [
store: :cookie,
key: "_solid_example_key",
signing_salt: "volt-example",
same_site: "Lax"
]

socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [session: @session_options]],
longpoll: [connect_info: [session: @session_options]]

plug Plug.Static,
at: "/",
from: :solid_example,
gzip: not code_reloading?,
only: SolidExampleWeb.static_paths(),
raise_on_missing_only: code_reloading?

if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
plug Volt.DevServer, root: "assets"
end

plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]

plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()

plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options
plug SolidExampleWeb.Router
end
Loading