Skip to content

Commit 31c28be

Browse files
committed
Converted md settings to localstorage, added preview resize
1 parent 38db3a2 commit 31c28be

File tree

8 files changed

+103
-103
lines changed

8 files changed

+103
-103
lines changed

app/Config/setting-defaults.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
'ui-shortcuts' => '{}',
3030
'ui-shortcuts-enabled' => false,
3131
'dark-mode-enabled' => env('APP_DEFAULT_DARK_MODE', false),
32-
'md-show-preview' => true,
33-
'md-scroll-sync' => true,
3432
'bookshelves_view_type' => env('APP_VIEWS_BOOKSHELVES', 'grid'),
3533
'bookshelf_view_type' => env('APP_VIEWS_BOOKSHELF', 'grid'),
3634
'books_view_type' => env('APP_VIEWS_BOOKS', 'grid'),

app/Http/Controllers/UserPreferencesController.php

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,25 +139,4 @@ public function updateCodeLanguageFavourite(Request $request)
139139
setting()->putForCurrentUser('code-language-favourites', implode(',', $currentFavorites));
140140
return response('', 204);
141141
}
142-
143-
/**
144-
* Update a boolean user preference setting.
145-
*/
146-
public function updateBooleanPreference(Request $request)
147-
{
148-
$allowedKeys = ['md-scroll-sync', 'md-show-preview'];
149-
$validated = $this->validate($request, [
150-
'name' => ['required', 'string'],
151-
'value' => ['required'],
152-
]);
153-
154-
if (!in_array($validated['name'], $allowedKeys)) {
155-
return response('Invalid boolean preference', 500);
156-
}
157-
158-
$value = $validated['value'] === 'true' ? 'true' : 'false';
159-
setting()->putForCurrentUser($validated['name'], $value);
160-
161-
return response('', 204);
162-
}
163142
}

resources/js/components/markdown-editor.js

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ export class MarkdownEditor extends Component {
1414

1515
this.display = this.$refs.display;
1616
this.input = this.$refs.input;
17-
this.settingContainer = this.$refs.settingContainer;
17+
this.divider = this.$refs.divider;
18+
this.displayWrap = this.$refs.displayWrap;
19+
20+
const settingContainer = this.$refs.settingContainer;
21+
const settingInputs = settingContainer.querySelectorAll('input[type="checkbox"]');
1822

1923
this.editor = null;
2024
initEditor({
@@ -23,11 +27,11 @@ export class MarkdownEditor extends Component {
2327
displayEl: this.display,
2428
inputEl: this.input,
2529
drawioUrl: this.getDrawioUrl(),
30+
settingInputs: Array.from(settingInputs),
2631
text: {
2732
serverUploadLimit: this.serverUploadLimitText,
2833
imageUploadError: this.imageUploadErrorText,
2934
},
30-
settings: this.loadSettings(),
3135
}).then(editor => {
3236
this.editor = editor;
3337
this.setupListeners();
@@ -76,30 +80,40 @@ export class MarkdownEditor extends Component {
7680
toolbarLabel.closest('.markdown-editor-wrap').classList.add('active');
7781
});
7882

79-
// Setting changes
80-
this.settingContainer.addEventListener('change', e => {
81-
const actualInput = e.target.parentNode.querySelector('input[type="hidden"]');
82-
const name = actualInput.getAttribute('name');
83-
const value = actualInput.getAttribute('value');
84-
window.$http.patch('/preferences/update-boolean', {name, value});
85-
this.editor.settings.set(name, value === 'true');
86-
});
87-
8883
// Refresh CodeMirror on container resize
8984
const resizeDebounced = debounce(() => this.editor.cm.refresh(), 100, false);
9085
const observer = new ResizeObserver(resizeDebounced);
9186
observer.observe(this.elem);
92-
}
9387

94-
loadSettings() {
95-
const settings = {};
96-
const inputs = this.settingContainer.querySelectorAll('input[type="hidden"]');
88+
this.handleDividerDrag();
89+
}
9790

98-
for (const input of inputs) {
99-
settings[input.getAttribute('name')] = input.value === 'true';
91+
handleDividerDrag() {
92+
this.divider.addEventListener('pointerdown', event => {
93+
const wrapRect = this.elem.getBoundingClientRect();
94+
const moveListener = (event) => {
95+
const xRel = event.pageX - wrapRect.left;
96+
const xPct = Math.min(Math.max(20, Math.floor((xRel / wrapRect.width) * 100)), 80);
97+
this.displayWrap.style.flexBasis = `${100-xPct}%`;
98+
this.editor.settings.set('editorWidth', xPct);
99+
};
100+
const upListener = (event) => {
101+
window.removeEventListener('pointermove', moveListener);
102+
window.removeEventListener('pointerup', upListener);
103+
this.display.style.pointerEvents = null;
104+
document.body.style.userSelect = null;
105+
this.editor.cm.refresh();
106+
};
107+
108+
this.display.style.pointerEvents = 'none';
109+
document.body.style.userSelect = 'none';
110+
window.addEventListener('pointermove', moveListener);
111+
window.addEventListener('pointerup', upListener);
112+
});
113+
const widthSetting = this.editor.settings.get('editorWidth');
114+
if (widthSetting) {
115+
this.displayWrap.style.flexBasis = `${100-widthSetting}%`;
100116
}
101-
102-
return settings;
103117
}
104118

105119
scrollToTextIfNeeded() {

resources/js/markdown/editor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export async function init(config) {
1919
const editor = {
2020
config,
2121
markdown: new Markdown(),
22-
settings: new Settings(config.settings),
22+
settings: new Settings(config.settingInputs),
2323
};
2424

2525
editor.actions = new Actions(editor);
@@ -39,8 +39,8 @@ export async function init(config) {
3939
* @property {Element} displayEl
4040
* @property {HTMLTextAreaElement} inputEl
4141
* @property {String} drawioUrl
42+
* @property {HTMLInputElement[]} settingInputs
4243
* @property {Object<String, String>} text
43-
* @property {Object<String, any>} settings
4444
*/
4545

4646
/**

resources/js/markdown/settings.js

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,62 @@
1-
import {kebabToCamel} from "../services/text";
2-
3-
41
export class Settings {
52

6-
constructor(initialSettings) {
7-
this.settingMap = {};
3+
constructor(settingInputs) {
4+
this.settingMap = {
5+
scrollSync: true,
6+
showPreview: true,
7+
editorWidth: 50,
8+
};
89
this.changeListeners = {};
9-
this.merge(initialSettings);
10+
this.loadFromLocalStorage();
11+
this.applyToInputs(settingInputs);
12+
this.listenToInputChanges(settingInputs);
13+
}
14+
15+
applyToInputs(inputs) {
16+
for (const input of inputs) {
17+
const name = input.getAttribute('name').replace('md-', '');
18+
input.checked = this.settingMap[name];
19+
}
20+
}
21+
22+
listenToInputChanges(inputs) {
23+
for (const input of inputs) {
24+
input.addEventListener('change', event => {
25+
const name = input.getAttribute('name').replace('md-', '');
26+
this.set(name, input.checked);
27+
});
28+
}
29+
}
30+
31+
loadFromLocalStorage() {
32+
const lsValString = window.localStorage.getItem('md-editor-settings');
33+
if (!lsValString) {
34+
return;
35+
}
36+
37+
const lsVals = JSON.parse(lsValString);
38+
for (const [key, value] of Object.entries(lsVals)) {
39+
if (value !== null && this.settingMap[key] !== undefined) {
40+
this.settingMap[key] = value;
41+
}
42+
}
1043
}
1144

1245
set(key, value) {
13-
key = this.normaliseKey(key);
1446
this.settingMap[key] = value;
47+
window.localStorage.setItem('md-editor-settings', JSON.stringify(this.settingMap));
1548
for (const listener of (this.changeListeners[key] || [])) {
1649
listener(value);
1750
}
1851
}
1952

2053
get(key) {
21-
return this.settingMap[this.normaliseKey(key)] || null;
22-
}
23-
24-
merge(settings) {
25-
for (const [key, value] of Object.entries(settings)) {
26-
this.set(key, value);
27-
}
54+
return this.settingMap[key] || null;
2855
}
2956

3057
onChange(key, callback) {
31-
key = this.normaliseKey(key);
32-
const listeners = this.changeListeners[this.normaliseKey(key)] || [];
58+
const listeners = this.changeListeners[key] || [];
3359
listeners.push(callback);
34-
this.changeListeners[this.normaliseKey(key)] = listeners;
35-
}
36-
37-
normaliseKey(key) {
38-
return kebabToCamel(key.replace('md-', ''));
60+
this.changeListeners[key] = listeners;
3961
}
4062
}

resources/sass/_forms.scss

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@
6060
outline: 0;
6161
}
6262
}
63-
.markdown-display, .markdown-editor-wrap {
64-
flex: 1;
65-
position: relative;
66-
}
6763
&.fullscreen {
6864
position: fixed;
6965
top: 0;
@@ -74,17 +70,22 @@
7470
}
7571

7672
.markdown-editor-wrap {
77-
display: flex;
78-
flex-direction: column;
7973
border-top: 1px solid #DDD;
8074
border-bottom: 1px solid #DDD;
8175
@include lightDark(border-color, #ddd, #000);
82-
width: 50%;
76+
position: relative;
77+
flex: 1;
8378
}
84-
8579
.markdown-editor-wrap + .markdown-editor-wrap {
86-
border-inline-start: 1px solid;
87-
@include lightDark(border-color, #ddd, #000);
80+
flex-basis: 50%;
81+
flex-shrink: 0;
82+
flex-grow: 0;
83+
}
84+
85+
.markdown-panel-divider {
86+
width: 2px;
87+
@include lightDark(background-color, #ddd, #000);
88+
cursor: col-resize;
8889
}
8990

9091
@include smaller-than($m) {
@@ -95,6 +96,7 @@
9596
width: 100%;
9697
max-width: 100%;
9798
flex-grow: 1;
99+
flex-basis: auto !important;
98100
}
99101
.editor-toolbar-label {
100102
float: none !important;

resources/views/pages/parts/markdown-editor.blade.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
option:markdown-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
66
class="flex-fill flex code-fill">
77

8-
<div class="markdown-editor-wrap active">
8+
<div class="markdown-editor-wrap active flex-container-column">
99
<div class="editor-toolbar flex-container-row items-stretch justify-space-between">
1010
<div class="editor-toolbar-label text-mono px-m py-xs flex-container-row items-center flex">
1111
<span>{{ trans('entities.pages_md_editor') }}</span>
@@ -20,11 +20,11 @@ class="flex-fill flex code-fill">
2020
<button refs="dropdown@toggle" class="text-button" type="button" title="{{ trans('common.more') }}">@icon('more')</button>
2121
<div refs="dropdown@menu markdown-editor@setting-container" class="dropdown-menu" role="menu">
2222
<div class="px-m">
23-
@include('form.toggle-switch', ['name' => 'md-show-preview', 'label' => trans('entities.pages_md_show_preview'), 'value' => setting()->getForCurrentUser('md-show-preview')])
23+
@include('form.custom-checkbox', ['name' => 'md-showPreview', 'label' => trans('entities.pages_md_show_preview'), 'value' => true, 'checked' => true])
2424
</div>
2525
<hr class="m-none">
2626
<div class="px-m">
27-
@include('form.toggle-switch', ['name' => 'md-scroll-sync', 'label' => trans('entities.pages_md_sync_scroll'), 'value' => setting()->getForCurrentUser('md-scroll-sync')])
27+
@include('form.custom-checkbox', ['name' => 'md-scrollSync', 'label' => trans('entities.pages_md_sync_scroll'), 'value' => true, 'checked' => true])
2828
</div>
2929
</div>
3030
</div>
@@ -40,14 +40,17 @@ class="flex-fill flex code-fill">
4040

4141
</div>
4242

43-
<div class="markdown-editor-wrap" @if(!setting()->getForCurrentUser('md-show-preview')) style="display: none;" @endif>
44-
<div class="editor-toolbar">
45-
<div class="editor-toolbar-label text-mono px-m py-xs">{{ trans('entities.pages_md_preview') }}</div>
43+
<div refs="markdown-editor@display-wrap" class="markdown-editor-wrap flex-container-row items-stretch" style="display: none">
44+
<div refs="markdown-editor@divider" class="markdown-panel-divider flex-fill"></div>
45+
<div class="flex-container-column flex flex-fill">
46+
<div class="editor-toolbar">
47+
<div class="editor-toolbar-label text-mono px-m py-xs">{{ trans('entities.pages_md_preview') }}</div>
48+
</div>
49+
<iframe src="about:blank"
50+
refs="markdown-editor@display"
51+
class="markdown-display flex flex-fill"
52+
sandbox="allow-same-origin"></iframe>
4653
</div>
47-
<iframe src="about:blank"
48-
refs="markdown-editor@display"
49-
class="markdown-display"
50-
sandbox="allow-same-origin"></iframe>
5154
</div>
5255
</div>
5356

tests/User/UserPreferencesTest.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,4 @@ public function test_update_code_language_favourite()
191191
$resp = $this->get($page->getUrl('/edit'));
192192
$resp->assertSee('option:code-editor:favourites="javascript,ruby"', false);
193193
}
194-
195-
public function test_update_boolean()
196-
{
197-
$editor = $this->getEditor();
198-
199-
$this->assertTrue(setting()->getUser($editor, 'md-show-preview'));
200-
201-
$resp = $this->actingAs($editor)->patch('/preferences/update-boolean', ['name' => 'md-show-preview', 'value' => 'false']);
202-
$resp->assertStatus(204);
203-
204-
$this->assertFalse(setting()->getUser($editor, 'md-show-preview'));
205-
}
206-
207-
public function test_update_boolean_rejects_unfamiliar_key()
208-
{
209-
$resp = $this->asEditor()->patch('/preferences/update-boolean', ['name' => 'md-donkey-show', 'value' => 'false']);
210-
$resp->assertStatus(500);
211-
}
212194
}

0 commit comments

Comments
 (0)