mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-13 00:14:33 +08:00
Update notebook and section name from the UI
This commit is contained in:
parent
565b87cfb6
commit
341d0a7ce6
4 changed files with 81 additions and 2 deletions
|
@ -4,6 +4,11 @@ import "phoenix_html";
|
|||
import { Socket } from "phoenix";
|
||||
import NProgress from "nprogress";
|
||||
import { LiveSocket } from "phoenix_live_view";
|
||||
import ContentEditable from "./content_editable";
|
||||
|
||||
const Hooks = {
|
||||
ContentEditable,
|
||||
};
|
||||
|
||||
const csrfToken = document
|
||||
.querySelector("meta[name='csrf-token']")
|
||||
|
@ -11,6 +16,7 @@ const csrfToken = document
|
|||
|
||||
const liveSocket = new LiveSocket("/live", Socket, {
|
||||
params: { _csrf_token: csrfToken },
|
||||
hooks: Hooks,
|
||||
});
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
|
39
assets/js/content_editable.js
Normal file
39
assets/js/content_editable.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* A hook used on [contenteditable] elements to update the specified
|
||||
* attribute with the element text.
|
||||
*
|
||||
* Configuration:
|
||||
*
|
||||
* * `data-update-attribute` - the name of the attribute to update when content changes
|
||||
*/
|
||||
const ContentEditable = {
|
||||
mounted() {
|
||||
this.attribute = this.el.dataset.updateAttribute;
|
||||
|
||||
this.__updateAttribute();
|
||||
|
||||
// Set the specified attribute on every content change
|
||||
this.el.addEventListener("input", (event) => {
|
||||
this.__updateAttribute();
|
||||
});
|
||||
|
||||
// Make sure only plain text is pasted
|
||||
this.el.addEventListener("paste", (event) => {
|
||||
event.preventDefault();
|
||||
const text = event.clipboardData.getData("text/plain");
|
||||
document.execCommand("insertText", false, text);
|
||||
});
|
||||
},
|
||||
|
||||
updated() {
|
||||
// The element has been re-rendered so we have to add the attribute back
|
||||
this.__updateAttribute();
|
||||
},
|
||||
|
||||
__updateAttribute() {
|
||||
const value = this.el.innerText.trim();
|
||||
this.el.setAttribute(this.attribute, value);
|
||||
},
|
||||
};
|
||||
|
||||
export default ContentEditable;
|
|
@ -7,7 +7,14 @@ defmodule LiveBookWeb.Section do
|
|||
<div class="flex justify-between items-center">
|
||||
<div class="flex space-x-2 items-center text-gray-600">
|
||||
<%= Icons.svg(:chevron_right, class: "h-8") %>
|
||||
<h2 class="text-3xl" contenteditable spellcheck="false"><%= @section.name %></h2>
|
||||
<h2 id="section-<%= @section.id %>-name"
|
||||
contenteditable
|
||||
spellcheck="false"
|
||||
phx-blur="set_section_name"
|
||||
phx-value-section_id="<%= @section.id %>"
|
||||
phx-hook="ContentEditable"
|
||||
data-update-attribute="phx-value-name"
|
||||
class="text-3xl"><%= @section.name %></h2>
|
||||
</div>
|
||||
<div class="flex space-x-2 items-center">
|
||||
<button phx-click="delete_section" phx-value-section_id="<%= @section.id %>" class="text-gray-600 hover:text-current">
|
||||
|
|
|
@ -30,7 +30,13 @@ defmodule LiveBookWeb.SessionLive do
|
|||
~L"""
|
||||
<div class="flex flex-grow max-h-full">
|
||||
<div class="w-1/5 bg-gray-100 border-r-2 border-gray-200">
|
||||
<h1 class="p-8 text-2xl" contenteditable spellcheck="false"><%= @data.notebook.name %></h1>
|
||||
<h1 id="notebook-name"
|
||||
contenteditable
|
||||
spellcheck="false"
|
||||
phx-blur="set_notebook_name"
|
||||
phx-hook="ContentEditable"
|
||||
data-update-attribute="phx-value-name"
|
||||
class="p-8 text-2xl"><%= @data.notebook.name %></h1>
|
||||
<div class="flex flex-col space-y-2 pl-4">
|
||||
<%= for section <- @data.notebook.sections do %>
|
||||
<div phx-click="select_section" phx-value-section_id="<%= section.id %>" class="py-2 px-4 rounded-l-md cursor-pointer text-gray-500 hover:text-current">
|
||||
|
@ -102,6 +108,27 @@ defmodule LiveBookWeb.SessionLive do
|
|||
{:noreply, assign(socket, focused_cell_id: cell_id)}
|
||||
end
|
||||
|
||||
def handle_event("set_notebook_name", %{"name" => name}, socket) do
|
||||
name = normalize_name(name)
|
||||
Session.set_notebook_name(socket.assigns.session_id, name)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("set_section_name", %{"section_id" => section_id, "name" => name}, socket) do
|
||||
name = normalize_name(name)
|
||||
Session.set_section_name(socket.assigns.session_id, section_id, name)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp normalize_name(name) do
|
||||
case String.trim(name) do
|
||||
"" -> "Untitled"
|
||||
name -> name
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:operation, operation}, socket) do
|
||||
case Session.Data.apply_operation(socket.assigns.data, operation) do
|
||||
|
|
Loading…
Add table
Reference in a new issue