-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add auto-save and restoration features to the editor
Resolves #5 by implementing real-time auto-save functionality and session restoration from local storage in the theme editor. Changes to SCSS files are now saved in real-time to the browser’s local storage. Upon returning to the theme editor, the system automatically restores the integrator's last session from local storage. This commit introduces the following changes: - Added a `ConfirmationDialog` component that prompts users to confirm before resetting the editor settings. This ensures that changes are not lost accidentally. - Introduced a `WorkspaceProvider` that manages the persistence and retrieval of the workspace state from local storage.
- Loading branch information
Showing
11 changed files
with
465 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { html, LitElement, unsafeCSS } from 'lit'; | ||
import { createRef, ref } from 'lit/directives/ref.js'; | ||
import confirmationDialogStyle from './confirmation-dialog.scss?inline'; | ||
|
||
/** | ||
* `ConfirmationDialog` is a LitElement that renders a modal dialog window which | ||
* can be used to confirm or reject an action. It encapsulates user interaction | ||
* in the form of "accept" or "cancel" actions. | ||
* | ||
* @fires ConfirmationDialog#close Fired when the dialog is closed, with `detail.accepted` indicating if the "accept" was clicked. | ||
* @fires ConfirmationDialog#open Fired when the dialog is opened. | ||
* | ||
* @part body - The container of the dialog's content. | ||
* @part actions - The container of the dialog's action buttons. | ||
* @part button - Parts for styling the buttons individually. | ||
* @part button cancel - Part for styling the cancel button. | ||
* @part button accept - Part for styling the accept button. | ||
* | ||
* @slot title - The dialog's title. Provides context about the dialog's purpose. | ||
* @slot content - The main content of the dialog, such as descriptions or questions. | ||
* @slot cancel - (Optional) Customizes the cancel button text. Defaults to "Cancel." | ||
* @slot accept - (Optional) Customizes the accept button text. Defaults to "Accept." | ||
* | ||
* @cssproperty [--dialog-padding=1em] - The padding inside the dialog. | ||
* @cssproperty [--dialog-background-color=#242424] - The background color of the dialog. | ||
* @cssproperty [--dialog-border=1px solid #666] - The border style for the dialog. | ||
* @cssproperty [--dialog-color=rgb(255 255 255 / 87%)] - The text color inside the dialog. | ||
* @cssproperty [--dialog-title-font-weight=600] - The font weight for the dialog title. | ||
* @cssproperty [--dialog-border-radius=0.5em] - The border radius of the dialog. | ||
* @cssproperty [--dialog-max-width=360px] - The maximum width of the dialog. | ||
* @cssproperty [--button-color=rgb(255 255 255 / 87%)] - The text color of the buttons. | ||
* @cssproperty [--button-border-radius=0.5em] - The border radius of the buttons. | ||
* @cssproperty [--accept-button-background=#9D4040] - The background color of the accept button. | ||
* @cssproperty [--accept-button-hover-background=#733030] - The background color of the accept button on hover. | ||
* @cssproperty [--cancel-button-background=rgb(255 255 255 / 0%)] - The background color of the cancel button. | ||
* @cssproperty [--cancel-button-hover-background=rgb(255 255 255 / 20%)] - The background color of the cancel button on hover. | ||
* @cssproperty [--cancel-button-hover-color=rgb(0 0 0 / 87%)] - The text color of the cancel button on hover. | ||
* | ||
* @example | ||
* <confirmation-dialog> | ||
* <span slot="title">Are you sure ?</span> | ||
* <span slot="content">This action is irreversible</span> | ||
* </confirmation-dialog> | ||
*/ | ||
class ConfirmationDialog extends LitElement { | ||
static styles = unsafeCSS(confirmationDialogStyle); | ||
|
||
static properties = { | ||
open: { | ||
type: Boolean, | ||
state: true | ||
}, | ||
accepted: { | ||
type: Boolean, | ||
state: true | ||
} | ||
}; | ||
|
||
/** | ||
* Reference to the dialog HTML element. | ||
* | ||
* @type {import('lit/directives/ref').Ref<HTMLDialogElement>} | ||
* @private | ||
*/ | ||
#dialog = createRef(); | ||
|
||
constructor() { | ||
super(); | ||
this.open = false; | ||
this.accepted = false; | ||
} | ||
|
||
render() { | ||
return html` | ||
<dialog ${ref(this.#dialog)}> | ||
<div part="body"> | ||
<slot name="title"></slot> | ||
<slot name="content"></slot> | ||
<div part="actions"> | ||
<button part="button cancel" @click=${() => this.#close(false)}> | ||
<slot name="cancel">Cancel</slot> | ||
</button> | ||
<button part="button accept" @click=${() => this.#close(true)}> | ||
<slot name="accept">Accept</slot> | ||
</button> | ||
</div> | ||
</div> | ||
</dialog> | ||
`; | ||
} | ||
|
||
#close(accepted = false) { | ||
this.open = false; | ||
this.accepted = accepted; | ||
} | ||
|
||
updated(_changedProperties) { | ||
super.updated(_changedProperties); | ||
|
||
if (_changedProperties.has('open')) { | ||
this.#updateDialog(); | ||
} | ||
} | ||
|
||
#updateDialog() { | ||
if (this.open) { | ||
this.#dialog.value.showModal(); | ||
/** | ||
* Custom event dispatched when the dialog is opened. | ||
* | ||
* @event ConfirmationDialog#open | ||
* @type {CustomEvent} | ||
*/ | ||
this.dispatchEvent(new CustomEvent('open')); | ||
} else { | ||
this.#dialog.value.close(); | ||
/** | ||
* Custom event dispatched when the dialog is closed. Includes whether the closure was an acceptance. | ||
* | ||
* @event ConfirmationDialog#close | ||
* @type {CustomEvent} | ||
* @property {Object} detail - The event detail object. | ||
* @property {boolean} detail.accepted - Indicates if the dialog was closed with an acceptance. | ||
*/ | ||
this.dispatchEvent(new CustomEvent('close', { detail: { accepted: this.accepted } })); | ||
} | ||
this.accepted = false; | ||
} | ||
|
||
/** | ||
* Toggles the dialog's open state. | ||
* | ||
* @param {boolean} [open] Specifies the desired open state. If not provided, | ||
* it toggles the current state. | ||
*/ | ||
toggle(open) { | ||
this.open = open ?? !this.open; | ||
} | ||
} | ||
|
||
customElements.define('confirmation-dialog', ConfirmationDialog); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
:host { | ||
--dialog-padding: 1em; | ||
--dialog-background-color: #242424; | ||
--dialog-border: 1px solid #666; | ||
--dialog-color: rgb(255 255 255 / 87%); | ||
--dialog-title-font-weight: 600; | ||
--dialog-border-radius: 0.5em; | ||
--dialog-max-width: 360px; | ||
--accept-button-background: #9D4040; | ||
--accept-button-hover-background: #733030; | ||
--cancel-button-background: rgb(255 255 255 / 0%); | ||
--cancel-button-hover-background: rgb(255 255 255 / 20%); | ||
--cancel-button-hover-color: rgb(0 0 0 / 87%); | ||
--button-color: rgb(255 255 255 / 87%); | ||
--button-border-radius: 0.5em; | ||
} | ||
|
||
dialog { | ||
max-width: var(--dialog-max-width); | ||
padding: var(--dialog-padding); | ||
overflow: visible; | ||
color: var(--dialog-color); | ||
background-color: var(--dialog-background-color); | ||
border: var(--dialog-border); | ||
border-radius: var(--dialog-border-radius); | ||
} | ||
|
||
dialog::backdrop { | ||
background: rgb(0 0 0 / 50%); | ||
} | ||
|
||
::slotted([slot="title"]) { | ||
font-weight: var(--dialog-title-font-weight); | ||
} | ||
|
||
::slotted([slot="content"]) { | ||
text-align: justify; | ||
} | ||
|
||
[part="body"] { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 0.5em; | ||
} | ||
|
||
[part="actions"] { | ||
display: flex; | ||
gap: 0.5em; | ||
justify-content: right; | ||
} | ||
|
||
[part~="button"] { | ||
padding: 1em; | ||
color: var(--button-color); | ||
background-color: transparent; | ||
border: none; | ||
border-radius: var(--button-border-radius); | ||
cursor: pointer; | ||
transition-timing-function: ease; | ||
transition-duration: 0.3s; | ||
transition-property: color, background-color; | ||
} | ||
|
||
[part="button cancel"] { | ||
background: var(--cancel-button-background); | ||
} | ||
|
||
[part="button cancel"]:hover { | ||
color: var(--cancel-button-hover-color); | ||
background-color: var(--cancel-button-hover-background); | ||
} | ||
|
||
[part="button accept"] { | ||
background: var(--accept-button-background); | ||
} | ||
|
||
[part="button accept"]:hover { | ||
background-color: var(--accept-button-hover-background); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,66 @@ | ||
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 './components/confirmation-dialog.js'; | ||
import './components/preview-box.js'; | ||
import './components/css-editor.js'; | ||
import sassCompiler from './workspace/workspace.js'; | ||
import WorkspaceProvider from './workspace/workspace-provider.js'; | ||
|
||
// Preview Initialisation | ||
const preview = document.getElementById('preview'); | ||
const sourceInput = document.getElementById('src-input'); | ||
|
||
sourceInput.addEventListener('keyup', (event) => { | ||
const src = event.target.value; | ||
|
||
if (event.key === 'Enter' && src) { | ||
preview.mediaSrc = src; | ||
} | ||
}); | ||
|
||
preview.appliedCss = sassCompiler.compile(); | ||
|
||
// Editor Initialisation | ||
const editor = document.getElementById('editor'); | ||
let currentItem = sassCompiler.mainScss; | ||
|
||
editor.setValue(sassCompiler.mainScss.content); | ||
editor.addEventListener('value-changed', (event) => { | ||
currentItem.content = event.detail.value; | ||
preview.appliedCss = sassCompiler.compile(); | ||
WorkspaceProvider.saveWorkspace(sassCompiler.workspace); | ||
}); | ||
|
||
// Navigation Control | ||
const navigation = document.getElementById('navigation'); | ||
const navigationButton = document.getElementById('navigation-button'); | ||
const editor = document.getElementById('editor'); | ||
const preview = document.getElementById('preview'); | ||
const downloadButton = document.getElementById('download'); | ||
const sourceInput = document.getElementById('src-input'); | ||
|
||
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) => { | ||
currentItem.content = event.detail.value; | ||
preview.appliedCss = sassCompiler.compile(); | ||
}); | ||
navigationButton.label = sassCompiler.mainScss.name; | ||
|
||
// Download Control | ||
const downloadButton = document.getElementById('download-button'); | ||
|
||
downloadButton.addEventListener('click', () => sassCompiler.download()); | ||
|
||
sourceInput.addEventListener('keyup', (event) => { | ||
const src = event.target.value; | ||
// Reset Control | ||
const resetButton = document.getElementById('reset-button'); | ||
const confirmationDialog = document.getElementById('reset-confirmation-dialog'); | ||
|
||
if (event.key === 'Enter' && src) { | ||
preview.mediaSrc = src; | ||
resetButton.addEventListener('click', () => { | ||
confirmationDialog.toggle(); | ||
}); | ||
confirmationDialog.addEventListener('close', (event) => { | ||
if (event.detail.accepted) { | ||
WorkspaceProvider.clear(); | ||
window.location.reload(); | ||
} | ||
}); |
Oops, something went wrong.