Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Spike sidebar in liveview #294

Open
wants to merge 117 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
425892a
Save progress
cibernox Oct 25, 2024
7ccde3d
Proof of concept
cibernox Oct 25, 2024
bc69d1d
This works better
cibernox Oct 25, 2024
939546f
Add some more sample stuff
cibernox Oct 25, 2024
6676da9
Add small margin
cibernox Oct 25, 2024
aa71a25
Add some more functionality
cibernox Nov 3, 2024
fdeacff
Save progress. Can add new attribute forms, but they don't do anything
cibernox Nov 8, 2024
d7e59ca
Extract a few components
cibernox Nov 8, 2024
3ec8801
Move logic to update hook
cibernox Nov 8, 2024
174528d
Extract add attribute button
cibernox Nov 8, 2024
63790f7
Fix deleting attributes
cibernox Nov 8, 2024
0e30f59
Keep cleaning things
cibernox Nov 8, 2024
d518fb7
Remove button that shouldn't be there
cibernox Nov 8, 2024
29134be
Buttons aligned
cibernox Nov 8, 2024
65da143
Save progress
cibernox Nov 8, 2024
3d510e3
Save progress
cibernox Nov 10, 2024
34bc259
Merge branch 'main' into spike-sidebar-in-liveview
leandrocp Nov 21, 2024
e181380
mix format
leandrocp Nov 21, 2024
984cd4b
remove unnecessary `use`
leandrocp Nov 21, 2024
ae21fc8
small fixes
leandrocp Nov 21, 2024
2e0bc0a
WIP: parent <form>
leandrocp Nov 21, 2024
91d96e4
assets.build
leandrocp Nov 21, 2024
3e2a326
<form> with add/remove buttons
leandrocp Nov 21, 2024
7654921
WIP: visual controls
leandrocp Nov 21, 2024
50ddaab
Merge branch 'main' into spike-sidebar-in-liveview
cibernox Nov 27, 2024
4ad05d4
Bring file from main
cibernox Nov 28, 2024
df7daea
Update mix lockfile
cibernox Nov 28, 2024
0daa2dc
I think this could work
cibernox Nov 28, 2024
0678c57
Updating tailwind did this
cibernox Nov 28, 2024
3c144aa
This works
cibernox Nov 28, 2024
41a9ddc
It works
cibernox Nov 28, 2024
6596509
New classes to at the end
cibernox Nov 28, 2024
63787ef
Fix mistake appending to list
cibernox Nov 28, 2024
f374a53
Simplify
cibernox Nov 28, 2024
98c77f9
Class controller looks a bit better
cibernox Nov 28, 2024
2845c20
Starts to look nice
cibernox Nov 28, 2024
5d57422
Adding classes works
cibernox Nov 28, 2024
7cb97e5
Use JS hooks
cibernox Nov 28, 2024
f5ee712
Implement deleting classes
cibernox Nov 28, 2024
c7e37f6
Removing classes works now
cibernox Nov 28, 2024
9d8a27c
Save progress
cibernox Nov 28, 2024
d3dc7ff
assets.build
leandrocp Nov 29, 2024
9f380b6
remove app.css
leandrocp Nov 29, 2024
3378684
mix format
leandrocp Nov 29, 2024
475e7e5
It looks better now
cibernox Nov 29, 2024
057e48f
wip - reorg code and tests
leandrocp Nov 29, 2024
117ec5f
tests
leandrocp Nov 29, 2024
747721d
more fixes and optimizations
leandrocp Nov 29, 2024
aed0090
selected_ast_element_id -> selected_element_path
leandrocp Nov 29, 2024
e82b4a3
revert :updated_event msg format
leandrocp Nov 29, 2024
b147838
add throttle
leandrocp Nov 29, 2024
ddc4137
FIXME
leandrocp Nov 29, 2024
3489913
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Nov 29, 2024
fb8ef0e
Almost working
cibernox Nov 29, 2024
8468d6b
Clean things a bit
cibernox Nov 29, 2024
754f762
Save progress
cibernox Nov 29, 2024
725ce85
This doesn't work yet
cibernox Nov 29, 2024
4391d80
This works but it's wonky
cibernox Dec 1, 2024
13d02ce
Hide "Add attribute" button while an attribute is being added already.
cibernox Dec 2, 2024
f6b8bf6
mix format
leandrocp Dec 2, 2024
51d0f1e
Save progress
cibernox Dec 2, 2024
3989249
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Dec 2, 2024
67183e3
Fix bad merge
cibernox Dec 2, 2024
e6fa66b
This works
cibernox Dec 2, 2024
f2ab097
Deleting attributes works
cibernox Dec 2, 2024
cdad8d7
format
leandrocp Dec 2, 2024
bc59803
comment out unused functionn
leandrocp Dec 2, 2024
a55ce63
Merge branch 'main' into spike-sidebar-in-liveview
leandrocp Dec 2, 2024
f0f7f61
cleanup
leandrocp Dec 2, 2024
94c5798
cleanup
leandrocp Dec 2, 2024
56b72cf
auto format code
leandrocp Dec 2, 2024
4070217
refactor: replace repeated sections with ControlSection component for…
cibernox Dec 3, 2024
ea18d08
auto format code
cibernox Dec 3, 2024
733253e
Add button to clear selection
cibernox Dec 3, 2024
ae431e9
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Dec 3, 2024
8317723
fix deleted changes
leandrocp Dec 3, 2024
15181d8
auto format code
leandrocp Dec 3, 2024
00ed940
ignore lexical lsp
leandrocp Dec 3, 2024
77308c1
assets.build
leandrocp Dec 3, 2024
e5cb5ba
replace twix with turboprop
leandrocp Dec 3, 2024
092f275
Improve a couple details
cibernox Dec 3, 2024
bbb12b0
Converting form. No errors are shown
cibernox Dec 3, 2024
e212255
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Dec 3, 2024
5c8eeaf
auto format code
cibernox Dec 3, 2024
bc4a035
Ooopsie
cibernox Dec 3, 2024
f1d6619
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Dec 3, 2024
57432f3
Save progress towards showing validation errors
cibernox Dec 4, 2024
7001835
auto format code
cibernox Dec 4, 2024
5090a65
Merge branch 'main' into spike-sidebar-in-liveview
cibernox Dec 13, 2024
1d43f5e
Move shared logic to its own method
cibernox Dec 13, 2024
4049255
Don't use macros, just forward the message
cibernox Dec 13, 2024
eba6333
Fix bad alias
cibernox Dec 13, 2024
744c036
Merge branch 'main' into spike-sidebar-in-liveview
APB9785 Dec 13, 2024
ca91d35
fix warning
APB9785 Dec 13, 2024
b8209d7
update import
APB9785 Dec 13, 2024
d701f1f
Validations work now
cibernox Dec 16, 2024
6fb9e76
auto format code
cibernox Dec 16, 2024
dd783c2
Bump cross-spawn from 7.0.3 to 7.0.6 (#315)
dependabot[bot] Dec 13, 2024
05ce136
Merge branch 'main' into spike-sidebar-in-liveview
leandrocp Dec 16, 2024
9a54341
fix merge
leandrocp Dec 16, 2024
ffebee2
review properties sidebar
leandrocp Dec 17, 2024
e54f1bb
add changeset/form to IdControl
leandrocp Dec 17, 2024
96cece3
fix new
leandrocp Dec 17, 2024
ba8c281
Merge branch 'main' into spike-sidebar-in-liveview
leandrocp Dec 18, 2024
4e2100c
assets.build
leandrocp Dec 18, 2024
039b739
move to VisualEditor namespace
leandrocp Dec 18, 2024
434b201
Make the edit end delete buttons display properly
cibernox Dec 18, 2024
4295f39
comment out undefined StationUI.Gettext
leandrocp Dec 18, 2024
3fce1b5
Small improvements
cibernox Dec 18, 2024
a71923b
The root is not an editable element
cibernox Dec 18, 2024
cc7e1a2
PoC test visual editor controls
leandrocp Dec 18, 2024
9075476
Make visual editor take all the available space it can
cibernox Dec 18, 2024
06969da
Merge branch 'spike-sidebar-in-liveview' of github.com:BeaconCMS/beac…
cibernox Dec 18, 2024
5c5847e
assets.build
leandrocp Dec 18, 2024
c038a26
update beacon
leandrocp Dec 19, 2024
5c00c52
small fixes to sidebar layout
leandrocp Dec 19, 2024
1652183
delete unused file
leandrocp Dec 19, 2024
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
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ locals_without_parens = [
]

[
import_deps: [:phoenix, :beacon],
import_deps: [:phoenix, :phoenix_live_view, :ecto, :beacon],
plugins: [Phoenix.LiveView.HTMLFormatter],
migrate_eex_to_curly_interpolation: false,
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"],
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ erl_crash.dump
beacon_live_admin-*.tar

# Ignore assets that are produced by build tools.
/assets/css/app.css
/priv/static/assets/

# Ignore digested assets cache.
Expand Down
6 changes: 5 additions & 1 deletion assets/js/beacon_live_admin.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import topbar from "../vendor/topbar"
import { CodeEditorHook } from "../../deps/live_monaco_editor/priv/static/live_monaco_editor.esm"
import { getHooks } from "live_svelte"
import visualEditorHooks from "../../lib/beacon/live_admin/components/visual_editor/hooks"
import * as Components from "../svelte/**/*.svelte"

let Hooks = {}

Hooks.CodeEditorHook = CodeEditorHook

topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300))
window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide())
Expand Down Expand Up @@ -48,7 +52,7 @@ window.addEventListener("beacon_admin:clipcopy", (event) => {
let socketPath = document.querySelector("html").getAttribute("phx-socket") || "/live"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveView.LiveSocket(socketPath, Phoenix.Socket, {
hooks: { ...getHooks(Components), ...Hooks },
hooks: { ...getHooks(Components), ...visualEditorHooks, ...Hooks },
params: { _csrf_token: csrfToken },
})
liveSocket.connect()
Expand Down
31 changes: 31 additions & 0 deletions assets/svelte/components/GoToParentButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { setSelection, selectedAstElementId } from "$lib/stores/page"
import { getParentNodeId } from "$lib/utils/ast-helpers"

function selectParentNode() {
let parentId = getParentNodeId($selectedAstElementId)
setSelection(parentId)
}
</script>

<button type="button" class="absolute p-2 top-2 right-9 group" on:click={selectParentNode}>
<span class="sr-only">Up one level</span>
<span
class="absolute opacity-0 invisible right-9 min-w-[100px] bg-amber-100 py-1 px-1.5 rounded text-xs text-medium transition group-hover:opacity-100 group-hover:visible"
>Up one level</span
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6 hover:text-blue-700 active:text-blue-900"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12"
/>
</svg>
</button>
19 changes: 3 additions & 16 deletions assets/svelte/components/PropertiesSidebar.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { createEventDispatcher } from "svelte"
import Pill from "$lib/components/Pill.svelte"
import ResetSelectionButton from "$lib/components/ResetSelectionButton.svelte"
import SidebarSection from "$lib/components/SidebarSection.svelte"
import { draggedComponentDefinition } from "$lib/stores/dragAndDrop"
import { live } from "$lib/stores/live"
import {
page,
selectedAstElement,
selectedAstElementId,
findAstElement,
isAstElement,
setSelection,
resetSelection,
Expand Down Expand Up @@ -168,21 +168,8 @@
</svg>
</button>
{/if}
<button type="button" class="absolute p-2 top-2 right-1" on:click={resetSelection}>
<span class="sr-only">Close</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-6 h-6 hover:text-blue-700 active:text-blue-900"
>
<path
fill-rule="evenodd"
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
clip-rule="evenodd"
/>
</svg>
</button>
<ResetSelectionButton />
=
</div>
{#if attributesEditable}
<SidebarSection clearOnUpdate={true} on:update={addClasses} disableDelete={true} placeholder="Add new class">
Expand Down
19 changes: 19 additions & 0 deletions assets/svelte/components/ResetSelectionButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { resetSelection } from "$lib/stores/page"
</script>

<button type="button" class="absolute p-2 top-2 right-1" on:click={resetSelection}>
<span class="sr-only">Close</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-6 h-6 hover:text-blue-700 active:text-blue-900"
>
<path
fill-rule="evenodd"
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
clip-rule="evenodd"
/>
</svg>
</button>
10 changes: 2 additions & 8 deletions assets/svelte/components/UiBuilder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
export let tailwindConfig: string
export let tailwindInput: string
export let live

$: $pageStore = page
$: $tailwindConfigStore = tailwindConfig
$: $tailwindInputStore = tailwindInput
Expand All @@ -24,22 +25,15 @@
onDestroy(() => {
resetStores()
})

function addBasicComponentToTarget(e: CustomEvent) {
// This method is in PagePreview.
}
</script>

<Backdrop />
<div class="flex min-h-screen bg-gray-100" id="ui-builder-app-container" data-testid="app-container">
<Backdrop />
<!-- Left sidebar -->
<ComponentsSidebar {components} />

<!-- Main -->
<PagePreview />

<!-- Right sidebar -->
<PropertiesSidebar on:droppedIntoTarget={(e) => addBasicComponentToTarget(e.detail)} />

<SelectedElementFloatingMenu />
</div>
7 changes: 6 additions & 1 deletion assets/svelte/stores/page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { writable, derived, get } from "svelte/store"
import type { Writable, Readable } from "svelte/store"
import type { AstElement, AstNode, Page } from "$lib/types"
import { live } from "$lib/stores/live"

export const page: Writable<Page> = writable()
export const selectedAstElementId: Writable<string | undefined> = writable()
Expand All @@ -17,7 +18,11 @@ export const selectedAstElement: Readable<AstElement | undefined> = derived(
[page, selectedAstElementId],
([$page, $selectedAstElementId]) => {
if ($page && $selectedAstElementId) {
return findAstElement($page.ast, $selectedAstElementId)
const element = findAstElement($page.ast, $selectedAstElementId)
get(live).pushEvent("select_element", { path: $selectedAstElementId })
return element
} else {
get(live).pushEvent("select_element", { path: null })
}
},
)
Expand Down
3 changes: 2 additions & 1 deletion lib/beacon/live_admin/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ defmodule Beacon.LiveAdmin.Application do
children = [
Beacon.LiveAdmin.Registry,
{Phoenix.PubSub, name: Beacon.LiveAdmin.PubSub},
Beacon.LiveAdmin.Cluster
Beacon.LiveAdmin.Cluster,
Turboprop.Cache
]

Supervisor.start_link(children, strategy: :one_for_one, name: Beacon.LiveAdmin.Supervisor)
Expand Down
14 changes: 8 additions & 6 deletions lib/beacon/live_admin/components/station_ui/html/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ defmodule Beacon.LiveAdmin.StationUI.HTML.Form do
"""
end

# FIXME: install StationUI to make use of StationUI.Gettext
@doc """
Translates an error message using gettext.
"""
def translate_error({msg, opts}) do
def translate_error({msg, _opts}) do
msg
# When using gettext, we typically pass the strings we want
# to translate as a static argument:
#
Expand All @@ -85,11 +87,11 @@ defmodule Beacon.LiveAdmin.StationUI.HTML.Form do
# dynamically, so we need to translate them by calling Gettext
# with our gettext backend as first argument. Translations are
# available in the errors.po file (as we use the "errors" domain).
if count = opts[:count] do
Gettext.dngettext(StationUI.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(StationUI.Gettext, "errors", msg, opts)
end
# if count = opts[:count] do
# Gettext.dngettext(StationUI.Gettext, "errors", msg, msg, count, opts)
# else
# Gettext.dgettext(StationUI.Gettext, "errors", msg, opts)
# end
end

@doc """
Expand Down
73 changes: 73 additions & 0 deletions lib/beacon/live_admin/components/visual_editor/class_control.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule Beacon.LiveAdmin.VisualEditor.ClassControl do
@moduledoc false

use Beacon.LiveAdmin.Web, :live_component
import Beacon.LiveAdmin.VisualEditor.Components
alias Beacon.LiveAdmin.VisualEditor

def render(assigns) do
~H"""
<div id={@id}>
<.control_section label="Classes">
<input type="text" class="w-full py-1 px-2 bg-gray-100 border-gray-100 rounded-md leading-6 text-sm" id={"#{@id}-input"} phx-hook="VisualEditorClassInput" data-target={@id} />
<div class="pt-3">
<div :for={css_class <- @classes} class="inline-flex items-center rounded-full bg-slate-700 text-white text-xs px-3 pr-0 m-1 leading-4">
<%= css_class %>
<button
class="p-2 rounded-full inline-block bg-slate-700 text-white hover:text-blue-400 active:text-blue-500"
type="button"
phx-click="delete_class"
phx-value-class={css_class}
phx-target={@myself}
>
<span class="sr-only">Delete class:</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3 h-3">
<path
fill-rule="evenodd"
d="M5.47 5.47a.75.75 0 0 1 1.06 0L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 0 1-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 0 1 0-1.06Z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</.control_section>
</div>
"""
end

def mount(socket) do
{:ok, socket}
end

def update(assigns, socket) do
%{element: element} = assigns

classes =
element
|> VisualEditor.element_class()
|> String.split(" ", trim: true)

{:ok,
socket
|> assign(assigns)
|> assign(classes: classes)}
end

def handle_event("add_class", %{"value" => new_class}, socket) do
class = VisualEditor.merge_class(socket.assigns.element, new_class)
send(self(), {:element_changed, {socket.assigns.element["path"], %{updated: %{"attrs" => %{"class" => class}}}}})
{:noreply, socket}
end

def handle_event("delete_class", %{"class" => deleted_class}, socket) do
class =
socket.assigns.classes
|> Enum.reject(&(&1 == deleted_class))
|> Enum.join(" ")

send(self(), {:element_changed, {socket.assigns.element["path"], %{updated: %{"attrs" => %{"class" => class}}}}})

{:noreply, socket}
end
end
15 changes: 15 additions & 0 deletions lib/beacon/live_admin/components/visual_editor/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default {
VisualEditorClassInput: {
mounted() {
this.el.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault()
const target = "#" + this.el.dataset.target
this.pushEventTo(target, "add_class", { value: this.el.value }, () => {
this.el.value = "" // Clear the input value
})
}
})
},
},
}
Loading
Loading