Skip to content

Commit

Permalink
Add notebook custom view (#2101)
Browse files Browse the repository at this point in the history
Co-authored-by: gitstart <bot@gitstart.com>
Co-authored-by: Hameed Abdulrahaman <hameedabdulrahamann@gmail.com>
Co-authored-by: gitstart_bot <gitstart_bot@users.noreply.github.com>
  • Loading branch information
4 people authored Jul 24, 2023
1 parent 103a904 commit 46a9452
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 52 deletions.
72 changes: 39 additions & 33 deletions assets/css/js_interop.css
Original file line number Diff line number Diff line change
Expand Up @@ -294,55 +294,61 @@ solely client-side operations.
@apply hidden;
}

/* === Views === */

[data-el-session][data-js-view="code-zen"] [data-el-section-headline],
[data-el-session][data-js-view="code-zen"] [data-el-section-subheadline],
[data-el-session][data-js-view="code-zen"]
[data-el-section-subheadline-collapsed],
[data-el-session][data-js-view="code-zen"] [data-el-cell][data-type="markdown"],
[data-el-session][data-js-view="code-zen"] [data-el-actions],
[data-el-session][data-js-view="code-zen"] [data-el-insert-buttons] {
@apply hidden;
/* === Session views === */

[data-el-session][data-js-view="code-zen"] [data-el-view-toggle="code-zen"] {
@apply text-green-bright-400;
}

[data-el-session][data-js-view="code-zen"] [data-el-sections-container] {
@apply space-y-0 mt-0;
[data-el-session][data-js-view="presentation"]
[data-el-view-toggle="presentation"] {
@apply text-green-bright-400;
}

[data-el-session][data-js-view="code-zen"] [data-el-view-toggle="code-zen"] {
[data-el-session][data-js-view="custom"] [data-el-view-toggle="custom"] {
@apply text-green-bright-400;
}

[data-el-session][data-js-view="presentation"]
[data-el-section-headline]:not([data-js-focused]),
[data-el-session][data-js-view="presentation"]
[data-el-section-subheadline]:not([data-js-focused]),
[data-el-session][data-js-view="presentation"]
[data-el-cell]:not([data-js-focused]),
[data-el-session][data-js-view="presentation"]
[data-el-js-view-iframes]
iframe:not([data-js-focused]) {
@apply opacity-10;
[data-el-session][data-js-view] [data-el-actions],
[data-el-session][data-js-view] [data-el-insert-buttons] {
@apply hidden;
}

[data-el-session][data-js-view="presentation"] [data-el-sidebar],
[data-el-session][data-js-view="presentation"] [data-el-side-panel],
[data-el-session][data-js-view="presentation"] [data-el-toggle-sidebar] {
[data-el-session]:is([data-js-view]) [data-el-views-disabled] {
@apply hidden;
}

[data-el-session][data-js-view="presentation"]
[data-el-view-toggle="presentation"] {
@apply text-green-bright-400;
[data-el-session]:not([data-js-view]) [data-el-views-enabled] {
@apply hidden;
}

[data-js-hide-output] [data-el-output] {
@apply hidden;
}

[data-el-session]:is([data-js-view="code-zen"], [data-js-view="presentation"])
[data-el-views-disabled] {
[data-js-hide-section] [data-el-section-headline],
[data-js-hide-section] [data-el-section-subheadline],
[data-js-hide-section] [data-el-section-subheadline-collapsed] {
@apply hidden;
}

[data-el-session]:not([data-js-view="code-zen"], [data-js-view="presentation"])
[data-el-views-enabled] {
[data-js-hide-section] [data-el-sections-container] {
@apply space-y-0 mt-0;
}

[data-js-hide-markdown] [data-el-cell][data-type="markdown"] {
@apply hidden;
}

[data-js-spotlight] [data-el-section-headline]:not([data-js-focused]),
[data-js-spotlight] [data-el-section-subheadline]:not([data-js-focused]),
[data-js-spotlight] [data-el-cell]:not([data-js-focused]),
[data-js-spotlight] [data-el-js-view-iframes] iframe:not([data-js-focused]) {
@apply opacity-10;
}

[data-js-spotlight] [data-el-sidebar],
[data-js-spotlight] [data-el-side-panel],
[data-js-spotlight] [data-el-toggle-sidebar] {
@apply hidden;
}
45 changes: 45 additions & 0 deletions assets/js/hooks/custom_view_settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { settingsStore } from "../lib/settings";

/**
* A hook for the custom view settings.
*/
const CustomViewSettings = {
mounted() {
const settings = settingsStore.get();

const customSectionCheckbox = this.el.querySelector(
`[name="show_section"][value="true"]`
);
const customMarkdownCheckbox = this.el.querySelector(
`[name="show_markdown"][value="true"]`
);
const customOutputCheckbox = this.el.querySelector(
`[name="show_output"][value="true"]`
);
const customSpotlightCheckbox = this.el.querySelector(
`[name="spotlight"][value="true"]`
);

customSectionCheckbox.checked = settings.custom_view_show_section;
customMarkdownCheckbox.checked = settings.custom_view_show_markdown;
customOutputCheckbox.checked = settings.custom_view_show_output;
customSpotlightCheckbox.checked = settings.custom_view_spotlight;

customSectionCheckbox.addEventListener("change", (event) => {
settingsStore.update({ custom_view_show_section: event.target.checked });
});
customMarkdownCheckbox.addEventListener("change", (event) => {
settingsStore.update({ custom_view_show_markdown: event.target.checked });
});
customOutputCheckbox.addEventListener("change", (event) => {
settingsStore.update({ custom_view_show_output: event.target.checked });
});
customSpotlightCheckbox.addEventListener("change", (event) => {
settingsStore.update({
custom_view_spotlight: event.target.checked,
});
});
},
};

export default CustomViewSettings;
2 changes: 2 additions & 0 deletions assets/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import UserForm from "./user_form";
import UtcDateTimeInput from "./utc_datetime_input";
import UtcTimeInput from "./utc_time_input";
import VirtualizedLines from "./virtualized_lines";
import CustomViewSettings from "./custom_view_settings";

export default {
AppAuth,
Expand All @@ -46,4 +47,5 @@ export default {
UtcDateTimeInput,
UtcTimeInput,
VirtualizedLines,
CustomViewSettings,
};
93 changes: 74 additions & 19 deletions assets/js/hooks/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { globalPubSub } from "../lib/pub_sub";
import monaco from "./cell_editor/live_editor/monaco";
import { leaveChannel } from "./js_view/channel";
import { isDirectlyEditable, isEvaluable } from "../lib/notebook";
import { settingsStore } from "../lib/settings";

/**
* A hook managing the whole session.
Expand Down Expand Up @@ -70,6 +71,7 @@ const Session = {
this.focusedId = null;
this.insertMode = false;
this.view = null;
this.viewOptions = null;
this.keyBuffer = new KeyBuffer();
this.clientsMap = {};
this.lastLocationReportByClientId = {};
Expand Down Expand Up @@ -415,17 +417,27 @@ const Session = {
} else if (keyBuffer.tryMatch(["N"])) {
this.insertCellAboveFocused("code");
} else if (keyBuffer.tryMatch(["m"])) {
!this.isViewCodeZen() && this.insertCellBelowFocused("markdown");
if (!this.view || this.viewOptions.showMarkdown) {
this.insertCellBelowFocused("markdown");
}
} else if (keyBuffer.tryMatch(["M"])) {
!this.isViewCodeZen() && this.insertCellAboveFocused("markdown");
if (!this.view || this.viewOptions.showMarkdown) {
this.insertCellAboveFocused("markdown");
}
} else if (keyBuffer.tryMatch(["v", "z"])) {
this.toggleView("code-zen");
} else if (keyBuffer.tryMatch(["v", "p"])) {
this.toggleView("presentation");
} else if (keyBuffer.tryMatch(["v", "c"])) {
this.toggleView("custom");
} else if (keyBuffer.tryMatch(["c"])) {
!this.isViewCodeZen() && this.toggleCollapseSection();
if (!this.view || this.viewOptions.showSection) {
this.toggleCollapseSection();
}
} else if (keyBuffer.tryMatch(["C"])) {
!this.isViewCodeZen() && this.toggleCollapseAllSections();
if (!this.view || this.viewOptions.showSection) {
this.toggleCollapseAllSections();
}
}
}
},
Expand Down Expand Up @@ -1083,12 +1095,39 @@ const Session = {
},

toggleView(view) {
if (this.view === view) {
this.view = null;
this.el.removeAttribute("data-js-view");
} else {
this.view = view;
this.el.setAttribute("data-js-view", view);
if (view === this.view) {
this.unsetView();

if (view === "custom") {
this.unsubscribeCustomViewFromSettings();
}
} else if (view === "code-zen") {
this.setView(view, {
showSection: false,
showMarkdown: false,
showOutput: true,
spotlight: false,
});
} else if (view === "presentation") {
this.setView(view, {
showSection: true,
showMarkdown: true,
showOutput: true,
spotlight: true,
});
} else if (view === "custom") {
this.unsubscribeCustomViewFromSettings = settingsStore.getAndSubscribe(
(settings) => {
this.setView(view, {
showSection: settings.custom_view_show_section,
showMarkdown: settings.custom_view_show_markdown,
showOutput: settings.custom_view_show_output,
spotlight: settings.custom_view_spotlight,
});
}
);

this.pushEvent("open_custom_view_settings");
}

// If nothing is focused, use the first cell in the viewport
Expand All @@ -1107,6 +1146,30 @@ const Session = {
}
},

setView(view, options) {
this.view = view;
this.viewOptions = options;

this.el.setAttribute("data-js-view", view);

this.el.toggleAttribute("data-js-hide-section", !options.showSection);
this.el.toggleAttribute("data-js-hide-markdown", !options.showMarkdown);
this.el.toggleAttribute("data-js-hide-output", !options.showOutput);
this.el.toggleAttribute("data-js-spotlight", options.spotlight);
},

unsetView() {
this.view = null;
this.viewOptions = null;

this.el.removeAttribute("data-js-view");

this.el.removeAttribute("data-js-hide-section");
this.el.removeAttribute("data-js-hide-markdown");
this.el.removeAttribute("data-js-hide-output");
this.el.removeAttribute("data-js-spotlight");
},

toggleCollapseSection() {
if (this.focusedId) {
const sectionId = this.getSectionIdByFocusableId(this.focusedId);
Expand Down Expand Up @@ -1152,7 +1215,7 @@ const Session = {

handleCellDeleted(cellId, siblingCellId) {
if (this.focusedId === cellId) {
if (this.isViewCodeZen()) {
if (this.view) {
const visibleSiblingId = this.ensureVisibleFocusableEl(siblingCellId);
this.setFocusedEl(visibleSiblingId);
} else {
Expand Down Expand Up @@ -1439,14 +1502,6 @@ const Session = {
getElement(name) {
return this.el.querySelector(`[data-el-${name}]`);
},

isViewCodeZen() {
return this.view === "code-zen";
},

isViewPresentation() {
return this.view === "presentation";
},
};

/**
Expand Down
25 changes: 25 additions & 0 deletions assets/js/lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const DEFAULT_SETTINGS = {
editor_font_size: EDITOR_FONT_SIZE.normal,
editor_theme: EDITOR_THEME.default,
editor_markdown_word_wrap: true,
custom_view_show_section: true,
custom_view_show_markdown: true,
custom_view_show_output: true,
custom_view_spotlight: false,
};

/**
Expand Down Expand Up @@ -57,10 +61,31 @@ class SettingsStore {
*
* The given function is called immediately with the current
* settings and then on every change.
*
* Returns a function that unsubscribes as a shorthand for
* `unsubscribe`.
*/
getAndSubscribe(callback) {
this._subscribers.push(callback);
callback(this._settings);

return () => {
this.unsubscribe(callback);
};
}

/**
* Unsubscribes the given function from updates.
*
* Note that you must pass the same function reference as you
* passed to `subscribe`.
*/
unsubscribe(callback) {
const index = this._subscribers.indexOf(callback);

if (index !== -1) {
this._subscribers.splice(index, 1);
}
}

_loadSettings() {
Expand Down
20 changes: 20 additions & 0 deletions lib/livebook_web/live/session_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,21 @@ defmodule LivebookWeb.SessionLive do
return_to={@self_path}
/>
</.modal>
<.modal
:if={@live_action == :custom_view_settings}
id="custom-view-modal"
show
width={:medium}
patch={@self_path}
>
<.live_component
module={LivebookWeb.SessionLive.CustomViewComponent}
id="custom"
return_to={@self_path}
session={@session}
/>
</.modal>
"""
end

Expand Down Expand Up @@ -1613,6 +1628,11 @@ defmodule LivebookWeb.SessionLive do
|> push_event("finish_file_drop", %{})}
end

def handle_event("open_custom_view_settings", %{}, socket) do
{:noreply,
push_patch(socket, to: ~p"/sessions/#{socket.assigns.session.id}/settings/custom-view")}
end

@impl true
def handle_info({:operation, operation}, socket) do
{:noreply, handle_operation(socket, operation)}
Expand Down
Loading

0 comments on commit 46a9452

Please sign in to comment.