Add notebook presentation view (#1852)

Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
Franklin Rakotomalala 2023-04-28 20:16:18 +02:00 committed by GitHub
parent 915bd0406b
commit 0eb0f417f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 78 deletions

View file

@ -65,7 +65,7 @@ solely client-side operations.
[data-el-section-headline]:not(:hover):not([data-js-focused])
[data-el-section-collapse-button],
[data-el-section]:not([data-js-collapsed]) [data-el-section-expand-button],
[data-el-session]:not([data-js-code-zen])
[data-el-session]:not([data-js-view="code-zen"])
[data-el-section][data-js-collapsed]
> [data-el-section-content],
[data-el-section]:not([data-js-collapsed])
@ -276,40 +276,55 @@ solely client-side operations.
@apply hidden;
}
[data-el-session][data-js-code-zen] [data-el-section-headline],
[data-el-session][data-js-code-zen] [data-el-section-subheadline],
[data-el-session][data-js-code-zen] [data-el-section-subheadline-collapsed],
[data-el-session][data-js-code-zen] [data-el-cell][data-type="markdown"],
[data-el-session][data-js-code-zen] [data-el-actions],
[data-el-session][data-js-code-zen] [data-el-insert-buttons] {
/* === 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;
}
[data-el-session][data-js-code-zen] [data-el-sections-container] {
[data-el-session][data-js-view="code-zen"] [data-el-sections-container] {
@apply space-y-0 mt-0;
}
[data-el-session][data-js-code-zen][data-js-no-outputs]
[data-el-outputs-container] {
[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="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="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] {
@apply hidden;
}
[data-el-session][data-js-code-zen][data-js-no-outputs]
[data-el-code-zen-outputs-toggle]
[data-label-hide] {
[data-el-session][data-js-view="presentation"]
[data-el-view-toggle="presentation"] {
@apply text-green-bright-400;
}
[data-el-session]:is([data-js-view="code-zen"], [data-js-view="presentation"])
[data-el-views-disabled] {
@apply hidden;
}
[data-el-session][data-js-code-zen]:not([data-js-no-outputs])
[data-el-code-zen-outputs-toggle]
[data-label-show] {
@apply hidden;
}
[data-el-session][data-js-code-zen] [data-el-code-zen-enable] {
@apply hidden;
}
[data-el-session]:not([data-js-code-zen]) [data-el-focus-mode-options] {
[data-el-session]:not([data-js-view="code-zen"], [data-js-view="presentation"])
[data-el-views-enabled] {
@apply hidden;
}

View file

@ -147,6 +147,11 @@ const JSView = {
// default timeout of 10s, so we increase it
30_000
);
this.unsubscribeFromCellEvents = globalPubSub.subscribe(
"navigation",
(event) => this.handleNavigationEvent(event)
);
},
updated() {
@ -167,6 +172,7 @@ const JSView = {
this.channel.push("disconnect", { ref: this.props.ref });
this.unsubscribeFromJSViewEvents();
this.unsubscribeFromCellEvents();
},
getProps() {
@ -454,6 +460,23 @@ const JSView = {
});
}
},
handleNavigationEvent(event) {
if (event.type === "element_focused") {
// If a parent focusable element is focused, mirror the attribute
// to the iframe element. This way if we need to apply style rules
// (such as opacity) to focused elements, we can target the iframe
// elements placed elsewhere in the DOM
const focusableEl = this.el.closest(`[data-focusable-id]`);
const focusableId = focusableEl ? focusableEl.dataset.focusableId : null;
this.iframe.toggleAttribute(
"data-js-focused",
focusableId === event.focusableId
);
}
},
};
export default JSView;

View file

@ -69,7 +69,7 @@ const Session = {
this.focusedId = null;
this.insertMode = false;
this.codeZen = false;
this.view = null;
this.keyBuffer = new KeyBuffer();
this.clientsMap = {};
this.lastLocationReportByClientId = {};
@ -131,20 +131,9 @@ const Session = {
this.handleCellIndicatorsClick(event)
);
this.getElement("code-zen-enable-button").addEventListener(
"click",
(event) => this.setCodeZen(true)
);
this.getElement("code-zen-disable-button").addEventListener(
"click",
(event) => this.setCodeZen(false)
);
this.getElement("code-zen-outputs-toggle").addEventListener(
"click",
(event) => this.el.toggleAttribute("data-js-no-outputs")
);
this.getElement("views").addEventListener("click", (event) => {
this.handleViewsClick(event);
});
this.getElement("section-toggle-collapse-all-button").addEventListener(
"click",
@ -421,15 +410,17 @@ const Session = {
} else if (keyBuffer.tryMatch(["N"])) {
this.insertCellAboveFocused("code");
} else if (keyBuffer.tryMatch(["m"])) {
!this.codeZen && this.insertCellBelowFocused("markdown");
!this.isViewCodeZen() && this.insertCellBelowFocused("markdown");
} else if (keyBuffer.tryMatch(["M"])) {
!this.codeZen && this.insertCellAboveFocused("markdown");
} else if (keyBuffer.tryMatch(["z"])) {
this.setCodeZen(!this.codeZen);
!this.isViewCodeZen() && 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(["c"])) {
!this.codeZen && this.toggleCollapseSection();
!this.isViewCodeZen() && this.toggleCollapseSection();
} else if (keyBuffer.tryMatch(["C"])) {
!this.codeZen && this.toggleCollapseAllSections();
!this.isViewCodeZen() && this.toggleCollapseAllSections();
}
}
},
@ -975,18 +966,27 @@ const Session = {
});
},
setCodeZen(enabled) {
this.codeZen = enabled;
handleViewsClick(event) {
const button = event.target.closest(`[data-el-view-toggle]`);
if (button) {
const view = button.getAttribute("data-el-view-toggle");
this.toggleView(view);
}
},
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 nothing is focused, use the first cell in the viewport
const focusedId = this.focusedId || this.nearbyFocusableId(null, 0);
if (enabled) {
this.el.setAttribute("data-js-code-zen", "");
} else {
this.el.removeAttribute("data-js-code-zen");
}
if (focusedId) {
const visibleId = this.ensureVisibleFocusableEl(focusedId);
@ -1034,7 +1034,6 @@ const Session = {
}
}
},
// Server event handlers
handleCellInserted(cellId) {
@ -1046,7 +1045,7 @@ const Session = {
handleCellDeleted(cellId, siblingCellId) {
if (this.focusedId === cellId) {
if (this.codeZen) {
if (this.isViewCodeZen()) {
const visibleSiblingId = this.ensureVisibleFocusableEl(siblingCellId);
this.setFocusedEl(visibleSiblingId);
} else {
@ -1333,6 +1332,14 @@ const Session = {
getElement(name) {
return this.el.querySelector(`[data-el-${name}]`);
},
isViewCodeZen() {
return this.view === "code-zen";
},
isViewPresentation() {
return this.view === "presentation";
},
};
/**

View file

@ -33,7 +33,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
class="flex flex-row-reverse sm:flex-col items-center justify-end p-2 sm:p-0 space-x-2 space-x-reverse sm:space-x-0 sm:space-y-2"
data-el-notebook-indicators
>
<.code_zen_indicator />
<.view_indicator />
<.persistence_indicator
file={@file}
dirty={@dirty}
@ -52,38 +52,36 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
"""
end
defp code_zen_indicator(assigns) do
defp view_indicator(assigns) do
~H"""
<span class="tooltip left" data-tooltip="Enter code zen (z)" data-el-code-zen-enable>
<button
class="icon-button icon-outlined-button border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
aria-label="enter code zen"
data-el-code-zen-enable-button
>
<.remix_icon icon="code-line" class="text-xl text-gray-400" />
</button>
</span>
<div data-el-focus-mode-options>
<.menu id="focus-mode-menu" position={:top_right}>
<div class="tooltip left" data-tooltip="Choose views to activate" data-el-views>
<.menu id="views-menu" position={:top_right}>
<:toggle>
<button
class="icon-button icon-outlined-button border-green-bright-300 hover:bg-green-bright-50 focus:bg-green-bright-50"
aria-label="code zen options"
class="icon-button icon-outlined-button border-gray-200 hover:bg-gray-100 focus:bg-gray-100"
aria-label="choose views to activate"
data-el-views-disabled
>
<.remix_icon icon="code-line" class="text-xl text-green-bright-400" />
<.remix_icon icon="layout-5-line" class="text-xl text-gray-400" />
</button>
<button
class="icon-button icon-outlined-button border-green-bright-300 hover:bg-green-bright-50 focus:bg-green-bright-50"
aria-label="choose views to activate"
data-el-views-enabled
>
<.remix_icon icon="layout-5-line" class="text-xl text-green-bright-400" />
</button>
</:toggle>
<.menu_item>
<button role="menuitem" data-el-code-zen-outputs-toggle>
<.remix_icon icon="layout-bottom-2-line" />
<span data-label-show>Show outputs</span>
<span data-label-hide>Hide outputs</span>
<button role="menuitem" data-el-view-toggle="code-zen">
<.remix_icon icon="code-line" />
<span>Code zen</span>
</button>
</.menu_item>
<.menu_item>
<button role="menuitem" data-el-code-zen-disable-button>
<.remix_icon icon="close-line" />
<span>Exit code zen</span>
<button role="menuitem" data-el-view-toggle="presentation">
<.remix_icon icon="slideshow-2-line" />
<span>Presentation</span>
</button>
</.menu_item>
</.menu>