Skip to content

Commit

Permalink
feat: add toggle pane button for theme editor file navigation
Browse files Browse the repository at this point in the history
Introduces a new `TogglePaneButton` component, aimed at displaying a virtual file system in order to
edit the multiple `scss` files found in the pillarbox default theme. When a file in the tree view is
selected, its content is loaded into the theme editor for modification and previewing.

***Example***
```html
<toggle-pane-button label="Display a message">
  <span>Hello World!</span>
</toggle-pane-button>
``
  • Loading branch information
jboix committed Apr 8, 2024
1 parent b3d3a36 commit 6e87939
Show file tree
Hide file tree
Showing 14 changed files with 303 additions and 37 deletions.
5 changes: 4 additions & 1 deletion .prebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* recursively includes all the files and their content to create json
* representation of the workspace for the theme editor.
*/
import { readdirSync, statSync, readFileSync, writeFileSync } from 'fs';
import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs';
import * as path from 'path';
import { join } from 'path';

const isDebugEnabled = process.argv.includes('--debug');
Expand Down Expand Up @@ -55,9 +56,11 @@ try {

// Define the output path for the generated workspace structure JSON file.
const outputPath = './src/assets/pillarbox-scss-workspace.json';
const outputDir = path.dirname(outputPath);

// Write the structured workspace to a JSON file, pretty-printed.
console.log(`Writing the workspace structure to ${outputPath}...`);
mkdirSync(outputDir, { recursive: true });
writeFileSync(outputPath, JSON.stringify(structure, null, 2), 'utf-8');

console.log(`Successfully wrote the workspace structure to ${outputPath}`);
Expand Down
7 changes: 6 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ <h1><img src="/pillarbox-logo.webp" class="logo" alt="Pillarbox logo"/> Pillarbo
<main>
<resizable-split-view>
<section slot="left">
<h2>Craft your theme</h2>
<div class="craft-title-container">
<h2>Craft your theme</h2>
<toggle-pane-button title="Select a file to edit" id="navigation-button">
<tree-view id="navigation"></tree-view>
</toggle-pane-button>
</div>
<css-editor id="editor"></css-editor>
</section>
<section slot="right">
Expand Down
6 changes: 6 additions & 0 deletions scss/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ footer {
}
}

.craft-title-container {
display: flex;
gap: 1em;
align-items: center;
}

/*
* TODO: Adjust .monaco-container dimensions to ensure full parent coverage.
*
Expand Down
37 changes: 23 additions & 14 deletions src/components/css-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ import * as monaco from 'monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';

const initialStyle = `
/* Turn the progress bar white */
.pillarbox-js .vjs-play-progress {
color: white;
background-color: white;
}
/* Show the scrubber */
.pillarbox-js .vjs-play-progress::before {
font-size: 0.9em;
}`.trim();

self.MonacoEnvironment = {
getWorker: function(_, label) {
if (label === 'css' || label === 'scss' || label === 'less') {
Expand Down Expand Up @@ -45,6 +33,14 @@ class CssEditor extends LitElement {
theme: { type: String }
};

/**
* Holds the initial value passed to this css editor.
*
* @type string
* @private
*/
#initialValue = '';

constructor() {
super();
this.container = createRef();
Expand Down Expand Up @@ -93,7 +89,20 @@ class CssEditor extends LitElement {
* @returns {String} The current content of the editor.
*/
getValue() {
return this.editor ? this.editor.getValue() : initialStyle;
return this.editor ? this.editor.getValue() : this.#initialValue;
}

/**
* Set the new content of the Monaco Editor.
*
* @param value {string} The new content fo the editor.
*/
setValue(value) {
if (!this.editor) {
this.#initialValue = value;
} else {
this.editor.setValue(value);
}
}

/**
Expand All @@ -104,7 +113,7 @@ class CssEditor extends LitElement {
super.firstUpdated(_changedProperties);

this.editor = monaco.editor.create(this.container.value, {
value: initialStyle,
value: this.#initialValue,
language: 'scss',
theme: this.getTheme(),
automaticLayout: true,
Expand Down
4 changes: 1 addition & 3 deletions src/components/preview-box.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { html, LitElement } from 'lit';
import { createRef, ref } from 'lit/directives/ref.js';
import pillarbox from '@srgssr/pillarbox-web';
import pbStyle from '@srgssr/pillarbox-web/dist/pillarbox.min.css?inline';

/**
* `PreviewBox` is a LitElement component that creates a pillarbox player
Expand Down Expand Up @@ -32,7 +31,6 @@ class PreviewBox extends LitElement {
*/
render() {
return html`
<style>${pbStyle}</style>
<style>${this.appliedCss}</style>
<video id="main-player"
class="pillarbox-js"
Expand All @@ -52,7 +50,7 @@ class PreviewBox extends LitElement {
// Initializes pillarbox with the video element and sets the video source.
this.player = pillarbox(this.pillarboxRef.value, { muted: true });
this.player.src({
src: 'urn:rts:video:14764404',
src: 'urn:rts:video:14318206',
type: 'srgssr/urn'
});
}
Expand Down
6 changes: 6 additions & 0 deletions src/components/resizable-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import resizableBarStyle from './resizable-bar.scss?inline';
*
* @part resizer - The resizer element.
*
* @cssproperty [--resizable-bar-margin-inline=1em] - The margin on the sides of the resizable bar.
* @cssproperty [--resizable-bar-resizer-bg-color=#575454] - The background color of the resizable bar.
* @cssproperty [--resizable-bar-resizer-width=0.3em] - The width of the resizable bar.
* @cssproperty [--resizable-bar-resizer-height=50%] - The height of the resizable bar.
* @cssproperty [--resizable-bar-resizer-border-radius=5px] - The border radius of the resizable bar.
*
* @example
* <resizable-bar></resizable-bar>
*/
Expand Down
95 changes: 95 additions & 0 deletions src/components/toggle-pane-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { html, LitElement, unsafeCSS } from 'lit';
import togglePaneButtonStyle from './toggle-pane-button.scss?inline';

/**
* A LitElement component that creates a toggle button to show or hides a popup pane.
* It changes its state when clicked, and can display custom content within a popup.
*
* @element toggle-pane-button
*
* @property {String} title - The title of the button, shown as a tooltip.
* @property {String} label - The visible label of the button.
* @property {Boolean} opened - Indicates whether the popup is currently visible. Reflects to attribute.
*
* @attribute {Boolean} opened - Reflects the `opened` property to an attribute. Determines the visibility of the popup.
*
* @slot - Default slot for the content of the popup.
*
* @part button - The button element that can be styled independently.
* @part popup - The popup element that appears or hides based on the `opened` state.
*
* @cssproperty [--button-background=#40729d] - The background color of the button.
* @cssproperty [--button-hover-background=#305273] - The background color of the button on hover.
* @cssproperty [--button-color=#fff] - The text color of the button.
* @cssproperty [--popup-background=#333] - The background color of the popup element.
* @cssproperty [--popup-border=1px solid #666] - The border style for the popup element.
* @cssproperty [--popup-border-radius=0.5em] - The border radius of the popup element.
* @cssproperty [--popup-z-index=1] - The z-index of the popup element, controlling its stack order.
*
* @example
* <toggle-pane-button label="Display a message">
* <span>Hello World!</span>
* </toggle-pane-button>
*/
class TogglePaneButton extends LitElement {
/**
* A private method to close the popup.
* @type {Function}
* @private
*/
#closePopup = () => { this.opened = false; };

static styles = unsafeCSS(togglePaneButtonStyle);

static properties = {
opened: { type: Boolean, reflect: true },
title: { type: String },
label: { type: String }
};

constructor() {
super();
this.opened = false;
}

render() {
return html`
<button part="button"
@click="${() => { this.opened = !this.opened; }}"
title="${this.title}">
${this.label}
</button>
<div part="popup">
<slot></slot>
</div>
`;
}

updated(_changedProperties) {
super.updated(_changedProperties);

if (_changedProperties.has('opened')) {
this.#togglePopup();
}
}

#togglePopup() {
if (this.opened) {
document.addEventListener('click', this.#closePopup, { once: true });
} else {
document.removeEventListener('click', this.#closePopup);
}
}

connectedCallback() {
super.connectedCallback();
this.addEventListener('click', e => e.stopPropagation());
}

disconnectedCallback() {
super.disconnectedCallback();
document.removeEventListener('click', this.#closePopup);
}
}

customElements.define('toggle-pane-button', TogglePaneButton);
57 changes: 57 additions & 0 deletions src/components/toggle-pane-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
:host {
--button-background: #40729d;
--button-hover-background: #305273;
--button-color: #fff;
--button-border-radius: 0.5em;
--popup-background: #333;
--popup-border: 1px solid #666;
--popup-border-radius: 0.5em;
--popup-z-index: 1;
}

[part="button"] {
position: relative; /* Needed for the chevron */
padding: 1em;
color: var(--button-foreground);
background: var(--button-background);
border: none;
border-radius: var(--button-border-radius);
cursor: pointer;
transition: background-color 0.3s ease; /* Smooth transition for hover effect */
}

[part="button"]::after {
display: inline-block;
width: 0.3em;
height: 0.3em;
border-right: 2px solid currentcolor;
border-bottom: 2px solid currentcolor;
transform: translate(50%, -50%) rotate(45deg);
transition: transform 0.3s ease;
content: "";
margin-inline: 0.5em;
}

[part="button"]:hover {
background-color: var(--button-hover-background);
}

[part="popup"] {
position: absolute;
z-index: var(--popup-z-index);
display: none;
margin-top: 5px;
padding: 0.7em;
background: var(--popup-background);
border: var(--popup-border);
border-radius: var(--popup-border-radius);
box-shadow: 0 2px 5px rgb(0 0 0 / 20%);
}

:host([opened]) [part="popup"] {
display: block;
}

:host([opened]) [part="button"]::after {
transform: translate(50%, -50%) rotate(225deg);
}
18 changes: 12 additions & 6 deletions src/components/tree-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ import { partMap } from './component-utils.js';
*
* @fires TreeView#selected Dispatched when a non-folder item is clicked, with the item's data as detail.
*
* @property {Array<TreeItem>} items Holds the tree structure data.
*
* @part item - The item element.
* @part item-name - The element displaying the item's name.
* @part root - The root element of the tree.
*
* @property {Array<TreeItem>} items Holds the tree structure data.
* @cssproperty [--tree-background-color=#333] - The background color of the tree component.
* @cssproperty [--tree-item-hover-color=#555] - The background color of a tree item when hovered.
* @cssproperty [--tree-item-selected-color=#666] - The background color of a tree item when selected.
* @cssproperty [--tree-folder-icon='📁'] - The icon used for folders in the tree.
* @cssproperty [--tree-file-icon='📄'] - The icon used for files in the tree.
* @cssproperty [--tree-icon-size=1em] - The size of the icons in the tree.
* @cssproperty [--tree-indentation=1.5em] - The indentation size for nested items in the tree.
*
* @example
* // Example of setting `items` for the TreeView component:
Expand All @@ -33,11 +41,9 @@ import { partMap } from './component-utils.js';
class TreeView extends LitElement {
static styles = unsafeCSS(treeViewStyle);

static get properties() {
return {
items: { type: Array }
};
}
static properties = {
items: { type: Array }
};

constructor() {
super();
Expand Down
2 changes: 1 addition & 1 deletion src/components/tree-view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
margin: 0;
padding: 0;
list-style-type: none;
background-color: var(--tree-background-color);
}

[part~="item"] {
Expand Down Expand Up @@ -59,7 +60,6 @@
}

/* Optional: Add a "selected" class to items when clicked to highlight the current selection */

[part~="item-name"].selected {
background-color: var(--tree-item-selected-color);
}
22 changes: 20 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,29 @@ import './components/css-editor.js';
import './components/resizable-split-view.js';
import './components/preview-box.js';
import './components/tree-view.js';
import './components/toggle-pane-button.js';
import sassCompiler from './workspace/workspace.js';

let currentItem = sassCompiler.mainScss;

const navigation = document.getElementById('navigation');
const navigationButton = document.getElementById('navigation-button');
const editor = document.getElementById('editor');
const preview = document.getElementById('preview');

preview.appliedCss = editor.getValue();
navigation.items = sassCompiler.workspace;
navigationButton.label = currentItem.name;
editor.setValue(currentItem.content);

navigation.addEventListener('selected', (event) => {
currentItem = event.detail;
navigationButton.label = currentItem.name;
editor.setValue(currentItem.content);
navigationButton.opened = false;
});

preview.appliedCss = sassCompiler.compile();
editor.addEventListener('value-changed', (event) => {
preview.appliedCss = event.detail.value;
currentItem.content = event.detail.value;
preview.appliedCss = sassCompiler.compile();
});
Loading

0 comments on commit 6e87939

Please sign in to comment.