client/posts: add expanders to edit sidebar
This commit is contained in:
		
							parent
							
								
									b9f2db1c63
								
							
						
					
					
						commit
						51ea06d837
					
				@ -222,3 +222,23 @@ a .access-key
 | 
			
		||||
[data-pseudo-content]:before {
 | 
			
		||||
    content: attr(data-pseudo-content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.expander
 | 
			
		||||
    &.collapsed
 | 
			
		||||
        margin-bottom: 1em
 | 
			
		||||
        &>*
 | 
			
		||||
            display: none
 | 
			
		||||
        &>header
 | 
			
		||||
            display: block
 | 
			
		||||
    header
 | 
			
		||||
        background: $active-tab-background-color
 | 
			
		||||
        line-height: 2em
 | 
			
		||||
        a
 | 
			
		||||
            padding: 0 0.5em
 | 
			
		||||
            display: block
 | 
			
		||||
            color: $inactive-link-color
 | 
			
		||||
            i
 | 
			
		||||
                float: right
 | 
			
		||||
                line-height: 2em
 | 
			
		||||
    .expander-content
 | 
			
		||||
        padding: 1em 0.5em
 | 
			
		||||
 | 
			
		||||
@ -303,8 +303,9 @@ $safety-unsafe = #F3985F
 | 
			
		||||
            margin-bottom: 0.5em
 | 
			
		||||
 | 
			
		||||
.post-view .edit-sidebar
 | 
			
		||||
    section
 | 
			
		||||
        margin-bottom: 1em
 | 
			
		||||
    .expander-content
 | 
			
		||||
        section:not(:last-child)
 | 
			
		||||
            margin-bottom: 1em
 | 
			
		||||
 | 
			
		||||
    .safety
 | 
			
		||||
        display: flex
 | 
			
		||||
 | 
			
		||||
@ -1,86 +1,77 @@
 | 
			
		||||
<div class='edit-sidebar'>
 | 
			
		||||
    <form autocomplete='off'>
 | 
			
		||||
        <div class='input'>
 | 
			
		||||
            <% if (ctx.canEditPostSafety) { %>
 | 
			
		||||
                <section class='safety'>
 | 
			
		||||
                    <label>Safety</label>
 | 
			
		||||
                    <%= ctx.makeRadio({
 | 
			
		||||
                        name: 'safety',
 | 
			
		||||
                        class: 'safety-safe',
 | 
			
		||||
                        value: 'safe',
 | 
			
		||||
                        selectedValue: ctx.post.safety,
 | 
			
		||||
                        text: 'Safe'}) %>
 | 
			
		||||
                    <%= ctx.makeRadio({
 | 
			
		||||
                        name: 'safety',
 | 
			
		||||
                        class: 'safety-sketchy',
 | 
			
		||||
                        value: 'sketchy',
 | 
			
		||||
                        selectedValue: ctx.post.safety,
 | 
			
		||||
                        text: 'Sketchy'}) %>
 | 
			
		||||
                    <%= ctx.makeRadio({
 | 
			
		||||
                        name: 'safety',
 | 
			
		||||
                        value: 'unsafe',
 | 
			
		||||
                        selectedValue: ctx.post.safety,
 | 
			
		||||
                        class: 'safety-unsafe',
 | 
			
		||||
                        text: 'Unsafe'}) %>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        <% if (ctx.canEditPostSafety) { %>
 | 
			
		||||
            <section class='safety'>
 | 
			
		||||
                <label>Safety</label>
 | 
			
		||||
                <%= ctx.makeRadio({
 | 
			
		||||
                    name: 'safety',
 | 
			
		||||
                    class: 'safety-safe',
 | 
			
		||||
                    value: 'safe',
 | 
			
		||||
                    selectedValue: ctx.post.safety,
 | 
			
		||||
                    text: 'Safe'}) %>
 | 
			
		||||
                <%= ctx.makeRadio({
 | 
			
		||||
                    name: 'safety',
 | 
			
		||||
                    class: 'safety-sketchy',
 | 
			
		||||
                    value: 'sketchy',
 | 
			
		||||
                    selectedValue: ctx.post.safety,
 | 
			
		||||
                    text: 'Sketchy'}) %>
 | 
			
		||||
                <%= ctx.makeRadio({
 | 
			
		||||
                    name: 'safety',
 | 
			
		||||
                    value: 'unsafe',
 | 
			
		||||
                    selectedValue: ctx.post.safety,
 | 
			
		||||
                    class: 'safety-unsafe',
 | 
			
		||||
                    text: 'Unsafe'}) %>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
            <% if (ctx.canEditPostRelations) { %>
 | 
			
		||||
                <section class='relations'>
 | 
			
		||||
                    <%= ctx.makeTextInput({
 | 
			
		||||
                        text: 'Relations',
 | 
			
		||||
                        name: 'relations',
 | 
			
		||||
                        placeholder: 'space-separated post IDs',
 | 
			
		||||
                        pattern: '^[0-9 ]*$',
 | 
			
		||||
                        value: ctx.post.relations.map(rel => rel.id).join(' '),
 | 
			
		||||
                    }) %>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        <% if (ctx.canEditPostRelations) { %>
 | 
			
		||||
            <section class='relations'>
 | 
			
		||||
                <%= ctx.makeTextInput({
 | 
			
		||||
                    text: 'Relations',
 | 
			
		||||
                    name: 'relations',
 | 
			
		||||
                    placeholder: 'space-separated post IDs',
 | 
			
		||||
                    pattern: '^[0-9 ]*$',
 | 
			
		||||
                    value: ctx.post.relations.map(rel => rel.id).join(' '),
 | 
			
		||||
                }) %>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
            <% if (ctx.canEditPostTags) { %>
 | 
			
		||||
                <section class='tags'>
 | 
			
		||||
                    <%= ctx.makeTextInput({
 | 
			
		||||
                        text: 'Tags',
 | 
			
		||||
                        value: ctx.post.tags.join(' '),
 | 
			
		||||
                    }) %>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        <% if (ctx.canEditPostFlags && ctx.post.type === 'video') { %>
 | 
			
		||||
            <section class='flags'>
 | 
			
		||||
                <label>Miscellaneous</label>
 | 
			
		||||
                <%= ctx.makeCheckbox({
 | 
			
		||||
                    text: 'Loop video',
 | 
			
		||||
                    name: 'loop',
 | 
			
		||||
                    checked: ctx.post.flags.includes('loop'),
 | 
			
		||||
                }) %>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
            <% if (ctx.canEditPostFlags && ctx.post.type === 'video') { %>
 | 
			
		||||
                <section class='flags'>
 | 
			
		||||
                    <label>Miscellaneous</label>
 | 
			
		||||
        <% if (ctx.canEditPostTags) { %>
 | 
			
		||||
            <section class='tags'>
 | 
			
		||||
                <%= ctx.makeTextInput({
 | 
			
		||||
                    value: ctx.post.tags.join(' '),
 | 
			
		||||
                }) %>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
                    <%= ctx.makeCheckbox({
 | 
			
		||||
                        text: 'Loop video',
 | 
			
		||||
                        name: 'loop',
 | 
			
		||||
                        checked: ctx.post.flags.includes('loop'),
 | 
			
		||||
                    }) %>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        <% if (ctx.canEditPostContent) { %>
 | 
			
		||||
            <section class='post-content'>
 | 
			
		||||
                <label>Content</label>
 | 
			
		||||
                <div class='dropper-container'></div>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
            <% if (ctx.canEditPostContent) { %>
 | 
			
		||||
                <section class='post-content'>
 | 
			
		||||
                    <label>Content</label>
 | 
			
		||||
 | 
			
		||||
                    <div class='dropper-container'></div>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
 | 
			
		||||
            <% if (ctx.canEditPostThumbnail) { %>
 | 
			
		||||
                <section class='post-thumbnail'>
 | 
			
		||||
                    <label>Thumbnail</label>
 | 
			
		||||
 | 
			
		||||
                    <div class='dropper-container'></div>
 | 
			
		||||
 | 
			
		||||
                    <a>Discard custom thumbnail</a>
 | 
			
		||||
                </section>
 | 
			
		||||
            <% } %>
 | 
			
		||||
        </div>
 | 
			
		||||
        <% if (ctx.canEditPostThumbnail) { %>
 | 
			
		||||
            <section class='post-thumbnail'>
 | 
			
		||||
                <label>Thumbnail</label>
 | 
			
		||||
                <div class='dropper-container'></div>
 | 
			
		||||
                <a>Discard custom thumbnail</a>
 | 
			
		||||
            </section>
 | 
			
		||||
        <% } %>
 | 
			
		||||
 | 
			
		||||
        <div class='messages'></div>
 | 
			
		||||
 | 
			
		||||
        <div class='buttons'>
 | 
			
		||||
            <input class='encourage' type='submit' value='Submit' class='submit'/>
 | 
			
		||||
        </div>
 | 
			
		||||
        <input type='submit' value='Submit' class='submit'/>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								client/js/controls/expander_control.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								client/js/controls/expander_control.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const ICON_CLASS_OPENED = 'fa-chevron-down';
 | 
			
		||||
const ICON_CLASS_CLOSED = 'fa-chevron-up';
 | 
			
		||||
 | 
			
		||||
class ExpanderControl {
 | 
			
		||||
    constructor(title, nodes) {
 | 
			
		||||
        this._title = title;
 | 
			
		||||
 | 
			
		||||
        nodes = Array.from(nodes).filter(n => n);
 | 
			
		||||
        if (!nodes.length) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const expanderNode = document.createElement('section');
 | 
			
		||||
        expanderNode.classList.add('expander');
 | 
			
		||||
 | 
			
		||||
        const toggleLinkNode = document.createElement('a');
 | 
			
		||||
        const toggleIconNode = document.createElement('i');
 | 
			
		||||
        toggleIconNode.classList.add('fa');
 | 
			
		||||
        toggleLinkNode.textContent = title;
 | 
			
		||||
        toggleLinkNode.appendChild(toggleIconNode);
 | 
			
		||||
        toggleLinkNode.addEventListener('click', e => this._evtToggleClick(e));
 | 
			
		||||
 | 
			
		||||
        const headerNode = document.createElement('header');
 | 
			
		||||
        headerNode.appendChild(toggleLinkNode);
 | 
			
		||||
        expanderNode.appendChild(headerNode);
 | 
			
		||||
 | 
			
		||||
        const expanderContentNode = document.createElement('div');
 | 
			
		||||
        expanderContentNode.classList.add('expander-content');
 | 
			
		||||
        expanderNode.appendChild(expanderContentNode);
 | 
			
		||||
 | 
			
		||||
        nodes[0].parentNode.insertBefore(expanderNode, nodes[0]);
 | 
			
		||||
 | 
			
		||||
        for (let node of nodes) {
 | 
			
		||||
            expanderContentNode.appendChild(node);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._expanderNode = expanderNode;
 | 
			
		||||
        this._toggleIconNode = toggleIconNode;
 | 
			
		||||
 | 
			
		||||
        expanderNode.classList.toggle(
 | 
			
		||||
            'collapsed',
 | 
			
		||||
            this._allStates[this._title] === undefined ?
 | 
			
		||||
                false :
 | 
			
		||||
                !this._allStates[this._title]);
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get _isOpened() {
 | 
			
		||||
        return !this._expanderNode.classList.contains('collapsed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get _allStates() {
 | 
			
		||||
        try {
 | 
			
		||||
            return JSON.parse(localStorage.getItem('expander')) || {};
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _save() {
 | 
			
		||||
        const newStates = Object.assign({}, this._allStates);
 | 
			
		||||
        newStates[this._title] = this._isOpened;
 | 
			
		||||
        localStorage.setItem('expander', JSON.stringify(newStates));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _evtToggleClick(e) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        this._expanderNode.classList.toggle('collapsed');
 | 
			
		||||
        this._save();
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _syncIcon() {
 | 
			
		||||
        if (this._isOpened) {
 | 
			
		||||
            this._toggleIconNode.classList.add(ICON_CLASS_OPENED);
 | 
			
		||||
            this._toggleIconNode.classList.remove(ICON_CLASS_CLOSED);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._toggleIconNode.classList.add(ICON_CLASS_CLOSED);
 | 
			
		||||
            this._toggleIconNode.classList.remove(ICON_CLASS_OPENED);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = ExpanderControl;
 | 
			
		||||
@ -5,6 +5,7 @@ const events = require('../events.js');
 | 
			
		||||
const misc = require('../util/misc.js');
 | 
			
		||||
const views = require('../util/views.js');
 | 
			
		||||
const TagInputControl = require('./tag_input_control.js');
 | 
			
		||||
const ExpanderControl = require('../controls/expander_control.js');
 | 
			
		||||
const FileDropperControl = require('../controls/file_dropper_control.js');
 | 
			
		||||
 | 
			
		||||
const template = views.getTemplate('post-edit-sidebar');
 | 
			
		||||
@ -32,6 +33,16 @@ class PostEditSidebarControl extends events.EventTarget {
 | 
			
		||||
            canFeaturePosts: api.hasPrivilege('posts:feature'),
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        new ExpanderControl(
 | 
			
		||||
            'Basic info',
 | 
			
		||||
            this._hostNode.querySelectorAll('.safety, .relations, .flags'));
 | 
			
		||||
        new ExpanderControl(
 | 
			
		||||
            'Tags',
 | 
			
		||||
            this._hostNode.querySelectorAll('.tags'));
 | 
			
		||||
        new ExpanderControl(
 | 
			
		||||
            'Content',
 | 
			
		||||
            this._hostNode.querySelectorAll('.post-content, .post-thumbnail'));
 | 
			
		||||
 | 
			
		||||
        if (this._formNode) {
 | 
			
		||||
            this._formNode.addEventListener('submit', e => this._evtSubmit(e));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user