add ability to bookmark posts and threads, courtesy of bitty
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, session
|
from flask import Flask, session, request
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from .models import Avatars, Users, PostHistory, Posts
|
from .models import Avatars, Users, PostHistory, Posts
|
||||||
from .auth import digest
|
from .auth import digest
|
||||||
@@ -131,6 +131,7 @@ def create_app():
|
|||||||
from app.routes.mod import bp as mod_bp
|
from app.routes.mod import bp as mod_bp
|
||||||
from app.routes.api import bp as api_bp
|
from app.routes.api import bp as api_bp
|
||||||
from app.routes.posts import bp as posts_bp
|
from app.routes.posts import bp as posts_bp
|
||||||
|
from app.routes.hyperapi import bp as hyperapi_bp
|
||||||
app.register_blueprint(app_bp)
|
app.register_blueprint(app_bp)
|
||||||
app.register_blueprint(topics_bp)
|
app.register_blueprint(topics_bp)
|
||||||
app.register_blueprint(threads_bp)
|
app.register_blueprint(threads_bp)
|
||||||
@@ -138,6 +139,7 @@ def create_app():
|
|||||||
app.register_blueprint(mod_bp)
|
app.register_blueprint(mod_bp)
|
||||||
app.register_blueprint(api_bp)
|
app.register_blueprint(api_bp)
|
||||||
app.register_blueprint(posts_bp)
|
app.register_blueprint(posts_bp)
|
||||||
|
app.register_blueprint(hyperapi_bp)
|
||||||
|
|
||||||
app.config['SESSION_COOKIE_SECURE'] = True
|
app.config['SESSION_COOKIE_SECURE'] = True
|
||||||
|
|
||||||
@@ -200,6 +202,15 @@ def create_app():
|
|||||||
for id_, text in matches
|
for id_, text in matches
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def _handle_404(e):
|
||||||
|
if request.path.startswith('/hyperapi/'):
|
||||||
|
return '<h1>not found</h1>', e.code
|
||||||
|
elif request.path.startswith('/api/'):
|
||||||
|
return {'error': 'not found'}, e.code
|
||||||
|
else:
|
||||||
|
return e
|
||||||
|
|
||||||
# this only happens at build time but
|
# this only happens at build time but
|
||||||
# build time is when updates are done anyway
|
# build time is when updates are done anyway
|
||||||
# sooo... /shrug
|
# sooo... /shrug
|
||||||
|
|||||||
@@ -371,6 +371,16 @@ class BookmarkCollections(Model):
|
|||||||
res = db.fetch_one(q, self.id)
|
res = db.fetch_one(q, self.id)
|
||||||
return int(res['pc'])
|
return int(res['pc'])
|
||||||
|
|
||||||
|
def has_thread(self, thread_id):
|
||||||
|
q = 'SELECT EXISTS(SELECT 1 FROM bookmarked_threads WHERE collection_id = ? AND thread_id = ?) as e'
|
||||||
|
res = db.fetch_one(q, self.id, int(thread_id))['e']
|
||||||
|
return int(res) == 1
|
||||||
|
|
||||||
|
def has_post(self, post_id):
|
||||||
|
q = 'SELECT EXISTS(SELECT 1 FROM bookmarked_posts WHERE collection_id = ? AND post_id = ?) as e'
|
||||||
|
res = db.fetch_one(q, self.id, int(post_id))['e']
|
||||||
|
return int(res) == 1
|
||||||
|
|
||||||
|
|
||||||
class BookmarkedPosts(Model):
|
class BookmarkedPosts(Model):
|
||||||
table = 'bookmarked_posts'
|
table = 'bookmarked_posts'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from flask import Blueprint, request, url_for
|
|||||||
from ..lib.babycode import babycode_to_html
|
from ..lib.babycode import babycode_to_html
|
||||||
from ..constants import REACTION_EMOJI
|
from ..constants import REACTION_EMOJI
|
||||||
from .users import is_logged_in, get_active_user
|
from .users import is_logged_in, get_active_user
|
||||||
from ..models import APIRateLimits, Threads, Reactions, Users, BookmarkCollections
|
from ..models import APIRateLimits, Threads, Reactions, Users, BookmarkCollections, BookmarkedThreads, BookmarkedPosts
|
||||||
from ..db import db
|
from ..db import db
|
||||||
|
|
||||||
bp = Blueprint("api", __name__, url_prefix="/api/")
|
bp = Blueprint("api", __name__, url_prefix="/api/")
|
||||||
@@ -139,3 +139,76 @@ def manage_bookmark_collections(user_id):
|
|||||||
|
|
||||||
|
|
||||||
return {'status': 'ok'}, 200
|
return {'status': 'ok'}, 200
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/bookmark-post/<post_id>')
|
||||||
|
def bookmark_post(post_id):
|
||||||
|
if not is_logged_in():
|
||||||
|
return {'error': 'not authorized', 'error_code': 401}, 401
|
||||||
|
|
||||||
|
operation = request.json.get('operation')
|
||||||
|
if operation == 'remove' and request.json.get('collection_id', '') == '':
|
||||||
|
return {'status': 'not modified'}, 304
|
||||||
|
collection_id = int(request.json.get('collection_id'))
|
||||||
|
post_id = int(post_id)
|
||||||
|
memo = request.json.get('memo', '')
|
||||||
|
|
||||||
|
if operation == 'move':
|
||||||
|
bm = BookmarkedPosts.find({'post_id': post_id})
|
||||||
|
if not bm:
|
||||||
|
BookmarkedPosts.create({
|
||||||
|
'post_id': post_id,
|
||||||
|
'collection_id': collection_id,
|
||||||
|
'note': memo,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
bm.update({
|
||||||
|
'collection_id': collection_id,
|
||||||
|
'note': memo,
|
||||||
|
})
|
||||||
|
elif operation == 'remove':
|
||||||
|
bm = BookmarkedPosts.find({'post_id': post_id})
|
||||||
|
if bm:
|
||||||
|
bm.delete()
|
||||||
|
else:
|
||||||
|
return {'error': 'bad request'}, 400
|
||||||
|
|
||||||
|
return {'status': 'ok'}, 200
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/bookmark-thread/<thread_id>')
|
||||||
|
def bookmark_thread(thread_id):
|
||||||
|
if not is_logged_in():
|
||||||
|
return {'error': 'not authorized', 'error_code': 401}, 401
|
||||||
|
|
||||||
|
operation = request.json.get('operation')
|
||||||
|
if operation == 'remove' and request.json.get('collection_id', '') == '':
|
||||||
|
return {'status': 'not modified'}, 304
|
||||||
|
collection_id = int(request.json.get('collection_id'))
|
||||||
|
thread_id = int(thread_id)
|
||||||
|
memo = request.json.get('memo', '')
|
||||||
|
|
||||||
|
if operation == 'move':
|
||||||
|
bm = BookmarkedThreads.find({'thread_id': thread_id})
|
||||||
|
if not bm:
|
||||||
|
BookmarkedThreads.create({
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'collection_id': collection_id,
|
||||||
|
'note': memo,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
bm.update({
|
||||||
|
'collection_id': collection_id,
|
||||||
|
'note': memo,
|
||||||
|
})
|
||||||
|
elif operation == 'remove':
|
||||||
|
bm = BookmarkedThreads.find({
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'note': memo,
|
||||||
|
})
|
||||||
|
if bm:
|
||||||
|
bm.delete()
|
||||||
|
else:
|
||||||
|
return {'error': 'bad request'}, 400
|
||||||
|
|
||||||
|
return {'status': 'ok'}, 200
|
||||||
|
|||||||
53
app/routes/hyperapi.py
Normal file
53
app/routes/hyperapi.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
from flask import Blueprint, render_template, abort, request
|
||||||
|
from .users import get_active_user, is_logged_in
|
||||||
|
from ..models import BookmarkCollections, BookmarkedPosts, BookmarkedThreads
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
bp = Blueprint('hyperapi', __name__, url_prefix='/hyperapi/')
|
||||||
|
|
||||||
|
def login_required(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def dec(*args, **kwargs):
|
||||||
|
if not is_logged_in():
|
||||||
|
abort(403)
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
return dec
|
||||||
|
|
||||||
|
def account_required(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def dec(*args, **kwargs):
|
||||||
|
if get_active_user().is_guest():
|
||||||
|
abort(403)
|
||||||
|
return view_func(*args, **kwargs)
|
||||||
|
return dec
|
||||||
|
|
||||||
|
@bp.errorhandler(403)
|
||||||
|
def handle_403(e):
|
||||||
|
return "<h1>forbidden</h1>", 403
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get('bookmarks-dropdown/<bookmark_type>')
|
||||||
|
@login_required
|
||||||
|
@account_required
|
||||||
|
def bookmarks_dropdown(bookmark_type):
|
||||||
|
collections = BookmarkCollections.findall({'user_id': get_active_user().id})
|
||||||
|
concept_id = request.args.get('id')
|
||||||
|
require_reload = bool(int(request.args.get('require_reload', default=0)))
|
||||||
|
if bookmark_type.lower() == 'thread':
|
||||||
|
selected = next(filter(lambda bc: bc.has_thread(concept_id), collections), None)
|
||||||
|
elif bookmark_type.lower() == 'post':
|
||||||
|
selected = next(filter(lambda bc: bc.has_post(concept_id), collections), None)
|
||||||
|
else:
|
||||||
|
abort(400)
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected:
|
||||||
|
if bookmark_type.lower() == 'thread':
|
||||||
|
memo = BookmarkedThreads.find({'collection_id': selected.id, 'thread_id': int(concept_id)}).note
|
||||||
|
else:
|
||||||
|
memo = BookmarkedPosts.find({'collection_id': selected.id, 'post_id': int(concept_id)}).note
|
||||||
|
else:
|
||||||
|
memo = ''
|
||||||
|
|
||||||
|
|
||||||
|
return render_template('components/bookmarks_dropdown.html', collections=collections, id=concept_id, selected=selected, type=bookmark_type, memo=memo, require_reload=require_reload)
|
||||||
@@ -10,20 +10,23 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<link rel="stylesheet" href="{{ ("/static/css/%s.css" % get_prefers_theme()) | cachebust }}">
|
<link rel="stylesheet" href="{{ ("/static/css/%s.css" % get_prefers_theme()) | cachebust }}">
|
||||||
<link rel="icon" type="image/png" href="/static/favicon.png">
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<script src="/static/js/vnd/bitty-5.1.0-rc6.min.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include 'common/topnav.html' %}
|
<bitty-5-1 data-connect="/static/js/bitties/pyrom-bitty.js">
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% include 'common/topnav.html' %}
|
||||||
{% if messages %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% for category, message in messages %}
|
{% if messages %}
|
||||||
{{ infobox(message, category) }}
|
{% for category, message in messages %}
|
||||||
{% endfor %}
|
{{ infobox(message, category) }}
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
{% endwith %}
|
{% endif %}
|
||||||
{% block content %}{% endblock %}
|
{% endwith %}
|
||||||
<footer class="darkbg">
|
{% block content %}{% endblock %}
|
||||||
<span>Pyrom commit <a href="{{ "https://git.poto.cafe/yagich/pyrom/commit/" + __commit }}">{{ __commit[:8] }}</a></span>
|
<footer class="darkbg">
|
||||||
</footer>
|
<span>Pyrom commit <a href="{{ "https://git.poto.cafe/yagich/pyrom/commit/" + __commit }}">{{ __commit[:8] }}</a></span>
|
||||||
|
</footer>
|
||||||
|
</bitty-5-1>
|
||||||
<script src="{{ "/static/js/ui.js" | cachebust }}"></script>
|
<script src="{{ "/static/js/ui.js" | cachebust }}"></script>
|
||||||
<script src="{{ "/static/js/date-fmt.js" | cachebust }}"></script>
|
<script src="{{ "/static/js/date-fmt.js" | cachebust }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -28,6 +28,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro bookmark_button(type, id, message = "Bookmark…", require_reload=false) %}
|
||||||
|
{% set bid = type[0] + id | string %}
|
||||||
|
<div class="bookmark-dropdown">
|
||||||
|
<button type="button" class="contain-svg inline icon" data-bookmark-type="{{type}}" data-send="showBookmarkMenu" data-concept-id="{{id}}" data-bookmark-id="{{bid}}">{{ icn_bookmark(20) }}{{ message | safe }}</button>
|
||||||
|
<div class="bookmark-dropdown-inner" data-receive="showBookmarkMenu" data-bookmark-id="{{bid}}" data-require-reload={{require_reload | int}}></div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro infobox(message, kind=InfoboxKind.INFO) %}
|
{% macro infobox(message, kind=InfoboxKind.INFO) %}
|
||||||
<div class="{{ "infobox " + InfoboxHTMLClass[kind] }}">
|
<div class="{{ "infobox " + InfoboxHTMLClass[kind] }}">
|
||||||
<span>
|
<span>
|
||||||
@@ -104,7 +112,8 @@
|
|||||||
post, render_sig = True, is_latest = False,
|
post, render_sig = True, is_latest = False,
|
||||||
editing = False, active_user = None, no_reply = false,
|
editing = False, active_user = None, no_reply = false,
|
||||||
Reactions = none, show_thread_title = false,
|
Reactions = none, show_thread_title = false,
|
||||||
show_bookmark = false, memo = None, bookmark_message = "Bookmark…"
|
show_bookmark = false, memo = None, bookmark_message = "Bookmark…",
|
||||||
|
reload_after_bookmark = false
|
||||||
) %}
|
) %}
|
||||||
{% set postclass = "post" %}
|
{% set postclass = "post" %}
|
||||||
{% if editing %}
|
{% if editing %}
|
||||||
@@ -182,7 +191,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if show_bookmark %}
|
{% if show_bookmark %}
|
||||||
<button type="button" class="contain-svg inline icon">{{ icn_bookmark(20) }}{{ bookmark_message | safe }}</button>
|
{{ bookmark_button(type="post", id=post.id, message=bookmark_message, require_reload=reload_after_bookmark)}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
23
app/templates/components/bookmarks_dropdown.html
Normal file
23
app/templates/components/bookmarks_dropdown.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% set bookmark_url = None %}
|
||||||
|
{% if type == 'post' %}
|
||||||
|
{% set bookmark_url = url_for('api.bookmark_post', post_id=id) %}
|
||||||
|
{% else %}
|
||||||
|
{% set bookmark_url = url_for('api.bookmark_thread', thread_id=id) %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="bookmarks-dropdown" data-bookmark-type="{{type}}" data-receive="saveBookmarks" data-bookmark-endpoint="{{bookmark_url}}" data-originally-contained-in="{{ selected.id if selected else ""}}" data-require-reload={{require_reload | int}}>
|
||||||
|
<div class="bookmarks-dropdown-header">
|
||||||
|
<span>Bookmark collections</span>
|
||||||
|
{% if not require_reload %}
|
||||||
|
<a href="{{ url_for('users.bookmarks', username=get_active_user().username) }}">View bookmarks</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="bookmark-dropdown-items-container">
|
||||||
|
{% for collection in collections %}
|
||||||
|
<div class="bookmark-dropdown-item {{ "selected" if selected and (selected.id | int) == (collection.id | int) else ""}}" data-send="selectBookmarkCollection" data-receive="selectBookmarkCollection" data-collection-id="{{collection.id}}">{{collection.name}} ({{ collection.get_posts_count() }}p, {{ collection.get_threads_count() }}t)</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<span>
|
||||||
|
<input type="text" placeholder="Memo" class="bookmark-memo-input" value="{{memo}}"></input>
|
||||||
|
<button type="button" data-send="saveBookmarks">Save</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{% from 'common/macros.html' import pager, babycode_editor_form, full_post %}
|
{% from 'common/macros.html' import pager, babycode_editor_form, full_post, bookmark_button %}
|
||||||
{% from 'common/icons.html' import icn_bookmark %}
|
{% from 'common/icons.html' import icn_bookmark %}
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{{ thread.title }}{% endblock %}
|
{% block title %}{{ thread.title }}{% endblock %}
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_bookmark %}
|
{% if can_bookmark %}
|
||||||
<button type="button" class="contain-svg inline icon">{{ icn_bookmark(20) }}Bookmark…</button>
|
{{ bookmark_button(type="thread", id=thread.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_lock %}
|
{% if can_lock %}
|
||||||
<form class="modform" action="{{ url_for("threads.lock", slug=thread.slug) }}" method="post">
|
<form class="modform" action="{{ url_for("threads.lock", slug=thread.slug) }}" method="post">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% from 'common/macros.html' import pager, timestamp %}
|
{% from 'common/macros.html' import pager, timestamp, bookmark_button %}
|
||||||
{% from 'common/icons.html' import icn_bookmark, icn_lock, icn_sticky %}
|
{% from 'common/icons.html' import icn_bookmark, icn_lock, icn_sticky %}
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}browsing topic {{ topic['name'] }}{% endblock %}
|
{% block title %}browsing topic {{ topic['name'] }}{% endblock %}
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{% if active_user and not active_user.is_guest() -%}
|
{% if active_user and not active_user.is_guest() -%}
|
||||||
<button class="thread-info-bookmark-button contain-svg icon" type="button">{{ icn_bookmark(20) }}Bookmark…</button>
|
{{ bookmark_button(type="thread", id=thread.id) }}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
{% from "common/macros.html" import accordion, full_post %}
|
{% from "common/macros.html" import accordion, full_post, bookmark_button %}
|
||||||
{% from "common/icons.html" import icn_bookmark %}
|
{% from "common/icons.html" import icn_bookmark %}
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}bookmarks{% endblock %}
|
{% block title %}bookmarks{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="darkbg inbox-container">
|
<div class="darkbg inbox-container">
|
||||||
|
<a class="linkbutton" href="{{ url_for('users.bookmark_collections', username=get_active_user().username) }}">Manage collections</a>
|
||||||
{% for collection in collections | sort(attribute='sort_order') %}
|
{% for collection in collections | sort(attribute='sort_order') %}
|
||||||
{% call(section) accordion(disabled=collection.is_empty()) %}
|
{% call(section) accordion(disabled=collection.is_empty()) %}
|
||||||
{% if section == 'header' %}
|
{% if section == 'header' %}
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
<i>{{ thread.note }}</i>
|
<i>{{ thread.note }}</i>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="contain-svg inline icon">{{ icn_bookmark(20) }}Manage…</button>
|
{{ bookmark_button(type='thread', id=thread.thread_id, message='Manage…', require_reload=true) }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
Posts{{" (no bookmarks)" if not collection.has_posts() else ""}}
|
Posts{{" (no bookmarks)" if not collection.has_posts() else ""}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% for post in collection.get_posts() %}
|
{% for post in collection.get_posts() %}
|
||||||
{{ full_post(post.get_post().get_full_post_view(), no_reply=false, render_sig=false, show_thread_title=true, show_bookmark=true, memo=post.note, bookmark_message="Manage…") }}
|
{{ full_post(post.get_post().get_full_post_view(), no_reply=false, render_sig=false, show_thread_title=true, show_bookmark=true, memo=post.note, bookmark_message="Manage…", reload_after_bookmark=true) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|||||||
@@ -1005,7 +1005,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus, select:focus
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorful-table tr th {
|
.colorful-table tr th {
|
||||||
@@ -1183,7 +1182,6 @@ ul, ol {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
margin: 10px 5px;
|
margin: 10px 5px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion.hidden {
|
.accordion.hidden {
|
||||||
@@ -1303,3 +1301,58 @@ footer {
|
|||||||
.babycode-guide-list {
|
.babycode-guide-list {
|
||||||
border-bottom: 1px dashed;
|
border-bottom: 1px dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-inner {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown {
|
||||||
|
background-color: #c1ceb1;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.25);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
min-width: 400px;
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin: 10px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: black;
|
||||||
|
background-color: rgb(177, 206, 204.5);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item:hover {
|
||||||
|
background-color: rgb(192.6, 215.8, 214.6);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected {
|
||||||
|
background-color: #beb1ce;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected:hover {
|
||||||
|
background-color: rgb(203, 192.6, 215.8);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%225%22%20width%3D%2214%22%20height%3D%2214%22%20rx%3D%222%22%20stroke%3D%22none%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-items-container {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1005,7 +1005,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus, select:focus
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorful-table tr th {
|
.colorful-table tr th {
|
||||||
@@ -1183,7 +1182,6 @@ ul, ol {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
margin: 10px 5px;
|
margin: 10px 5px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion.hidden {
|
.accordion.hidden {
|
||||||
@@ -1304,6 +1302,61 @@ footer {
|
|||||||
border-bottom: 1px dashed;
|
border-bottom: 1px dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-inner {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown {
|
||||||
|
background-color: #9b649b;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.25);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
min-width: 400px;
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin: 10px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #e6e6e6;
|
||||||
|
background-color: #3c283c;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item:hover {
|
||||||
|
background-color: rgb(109.2, 72.8, 109.2);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected {
|
||||||
|
background-color: #8a5584;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected:hover {
|
||||||
|
background-color: rgb(167.4843049327, 112.9156950673, 161.3067264574);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%225%22%20width%3D%2214%22%20height%3D%2214%22%20rx%3D%222%22%20stroke%3D%22none%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-items-container {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
#topnav {
|
#topnav {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border: 10px solid rgb(40, 40, 40);
|
border: 10px solid rgb(40, 40, 40);
|
||||||
|
|||||||
@@ -1005,7 +1005,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus, select:focus
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorful-table tr th {
|
.colorful-table tr th {
|
||||||
@@ -1183,7 +1182,6 @@ ul, ol {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
margin: 6px 3px;
|
margin: 6px 3px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion.hidden {
|
.accordion.hidden {
|
||||||
@@ -1304,6 +1302,61 @@ footer {
|
|||||||
border-bottom: 1px dashed;
|
border-bottom: 1px dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-inner {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown {
|
||||||
|
background-color: #f27a5a;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.25);
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
min-width: 400px;
|
||||||
|
padding: 6px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 0;
|
||||||
|
margin: 6px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 16px;
|
||||||
|
color: black;
|
||||||
|
background-color: #f27a5a;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item:hover {
|
||||||
|
background-color: rgb(244.6, 148.6, 123);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected {
|
||||||
|
background-color: #b54444;
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected:hover {
|
||||||
|
background-color: rgb(197.978313253, 103.221686747, 103.221686747);
|
||||||
|
}
|
||||||
|
.bookmark-dropdown-item.selected::before {
|
||||||
|
content: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%225%22%20width%3D%2214%22%20height%3D%2214%22%20rx%3D%222%22%20stroke%3D%22none%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-items-container {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
#topnav {
|
#topnav {
|
||||||
border-top-left-radius: 16px;
|
border-top-left-radius: 16px;
|
||||||
border-top-right-radius: 16px;
|
border-top-right-radius: 16px;
|
||||||
|
|||||||
64
data/static/js/bitties/pyrom-bitty.js
Normal file
64
data/static/js/bitties/pyrom-bitty.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
const bookmarkMenuHrefTemplate = '/hyperapi/bookmarks-dropdown'
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
async showBookmarkMenu(ev, el) {
|
||||||
|
if ((ev.target.dataset.bookmarkId === el.dataset.bookmarkId) && el.childElementCount === 0) {
|
||||||
|
const bookmarkMenuHref = `${bookmarkMenuHrefTemplate}/${ev.target.dataset.bookmarkType}?id=${ev.target.dataset.conceptId}&require_reload=${el.dataset.requireReload}`;
|
||||||
|
const res = await this.api.getHTML(bookmarkMenuHref);
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const frag = res.value;
|
||||||
|
el.appendChild(frag);
|
||||||
|
const menu = el.childNodes[0];
|
||||||
|
const bRect = el.getBoundingClientRect()
|
||||||
|
if (bRect.left < window.innerWidth - bRect.right) {
|
||||||
|
menu.style.right = 'unset';
|
||||||
|
}
|
||||||
|
} else if (el.childElementCount > 0) {
|
||||||
|
el.removeChild(el.childNodes[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectBookmarkCollection(ev, el) {
|
||||||
|
const clicked = ev.target;
|
||||||
|
|
||||||
|
if (clicked === el) {
|
||||||
|
if (clicked.classList.contains('selected')) {
|
||||||
|
clicked.classList.remove('selected');
|
||||||
|
} else {
|
||||||
|
clicked.classList.add('selected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
el.classList.remove('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveBookmarks(ev, el) {
|
||||||
|
const bookmarkHref = el.dataset.bookmarkEndpoint;
|
||||||
|
const collection = el.querySelector('.bookmark-dropdown-item.selected');
|
||||||
|
let data = {};
|
||||||
|
if (collection) {
|
||||||
|
data['operation'] = 'move';
|
||||||
|
data['collection_id'] = collection.dataset.collectionId;
|
||||||
|
data['memo'] = el.querySelector('.bookmark-memo-input').value;
|
||||||
|
} else {
|
||||||
|
data['operation'] = 'remove';
|
||||||
|
data['collection_id'] = el.dataset.originallyContainedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const requireReload = parseInt(el.dataset.requireReload) !== 0;
|
||||||
|
el.remove();
|
||||||
|
await fetch(bookmarkHref, options);
|
||||||
|
if (requireReload) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,14 @@ $MAIN_BG: color.scale($ACCENT_COLOR, $lightness: -10%, $saturation: -40%) !defau
|
|||||||
$BUTTON_COLOR: color.adjust($ACCENT_COLOR, $hue: 90) !default;
|
$BUTTON_COLOR: color.adjust($ACCENT_COLOR, $hue: 90) !default;
|
||||||
$BUTTON_COLOR_2: color.adjust($ACCENT_COLOR, $hue: 180) !default;
|
$BUTTON_COLOR_2: color.adjust($ACCENT_COLOR, $hue: 180) !default;
|
||||||
|
|
||||||
|
$BUTTON_COLOR_HOVER: color.scale($BUTTON_COLOR, $lightness: 20%) !default;
|
||||||
|
$BUTTON_COLOR_ACTIVE: color.scale($BUTTON_COLOR, $lightness: -10%, $saturation: -70%) !default;
|
||||||
|
$BUTTON_COLOR_DISABLED: color.scale($BUTTON_COLOR, $lightness: 30%, $saturation: -90%) !default;
|
||||||
|
|
||||||
|
$BUTTON_COLOR_2_HOVER: color.scale($BUTTON_COLOR_2, $lightness: 20%) !default;
|
||||||
|
$BUTTON_COLOR_2_ACTIVE: color.scale($BUTTON_COLOR_2, $lightness: -10%, $saturation: -70%) !default;
|
||||||
|
$BUTTON_COLOR_2_DISABLED: color.scale($BUTTON_COLOR_2, $lightness: 30%, $saturation: -90%) !default;
|
||||||
|
|
||||||
$ACCORDION_COLOR: color.adjust($ACCENT_COLOR, $hue: 140, $lightness: -10%, $saturation: -15%) !default;
|
$ACCORDION_COLOR: color.adjust($ACCENT_COLOR, $hue: 140, $lightness: -10%, $saturation: -15%) !default;
|
||||||
|
|
||||||
$DEFAULT_FONT_COLOR: black !default;
|
$DEFAULT_FONT_COLOR: black !default;
|
||||||
@@ -885,7 +893,7 @@ $colorful_table_margin: $MEDIUM_PADDING $ZERO_PADDING !default;
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: $colorful_table_margin;
|
margin: $colorful_table_margin;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
$colorful_table_th_color: $BUTTON_COLOR_2 !default;
|
$colorful_table_th_color: $BUTTON_COLOR_2 !default;
|
||||||
@@ -1095,7 +1103,7 @@ $accordion_margin: $MEDIUM_PADDING $SMALL_PADDING !default;
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: $accordion_border;
|
border: $accordion_border;
|
||||||
margin: $accordion_margin;
|
margin: $accordion_margin;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion.hidden {
|
.accordion.hidden {
|
||||||
@@ -1221,3 +1229,79 @@ $babycode_guide_list_border: 1px dashed !default;
|
|||||||
.babycode-guide-list {
|
.babycode-guide-list {
|
||||||
border-bottom: $babycode_guide_list_border;
|
border-bottom: $babycode_guide_list_border;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-dropdown-inner {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bookmarks_dropdown_background_color: $ACCENT_COLOR !default;
|
||||||
|
$bookmarks_dropdown_border_radius: $DEFAULT_BORDER_RADIUS !default;
|
||||||
|
$bookmarks_dropdown_border: $button_border !default;
|
||||||
|
$bookmarks_dropdown_shadow: 0 0 30px rgba(0, 0, 0, 0.25) !default;
|
||||||
|
$bookmarks_dropdown_min_width: 400px !default;
|
||||||
|
$bookmarks_dropdown_padding: $MEDIUM_PADDING !default;
|
||||||
|
.bookmarks-dropdown {
|
||||||
|
background-color: $bookmarks_dropdown_background_color;
|
||||||
|
border: $bookmarks_dropdown_border;
|
||||||
|
border-radius: $bookmarks_dropdown_border_radius;
|
||||||
|
box-shadow: $bookmarks_dropdown_shadow;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
min-width: $bookmarks_dropdown_min_width;
|
||||||
|
padding: $bookmarks_dropdown_padding;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bookmark_dropdown_item_padding: $MEDIUM_PADDING 0 !default;
|
||||||
|
$bookmark_dropdown_item_margin: $MEDIUM_PADDING 0 !default;
|
||||||
|
$bookmark_dropdown_item_font_color: $BUTTON_FONT_COLOR !default;
|
||||||
|
$bookmark_dropdown_item_background: $BUTTON_COLOR !default;
|
||||||
|
$bookmark_dropdown_item_background_hover: $BUTTON_COLOR_HOVER !default;
|
||||||
|
$bookmark_dropdown_item_background_selected: $BUTTON_COLOR_2 !default;
|
||||||
|
$bookmark_dropdown_item_background_selected_hover: $BUTTON_COLOR_2_HOVER !default;
|
||||||
|
$bookmark_dropdown_item_icon_size: 24px !default;
|
||||||
|
$bookmark_dropdown_item_icon_padding: 0 $MEDIUM_PADDING !default;
|
||||||
|
.bookmark-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
padding: $bookmark_dropdown_item_padding;
|
||||||
|
margin: $bookmark_dropdown_item_margin;
|
||||||
|
cursor: pointer;
|
||||||
|
border: $button_border;
|
||||||
|
border-radius: $button_border_radius;
|
||||||
|
color: $bookmark_dropdown_item_font_color;
|
||||||
|
|
||||||
|
background-color: $bookmark_dropdown_item_background;
|
||||||
|
&:hover {
|
||||||
|
background-color: $bookmark_dropdown_item_background_hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
// TODO: un-inline this once the bitty bug is fixed
|
||||||
|
content: url('data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E');
|
||||||
|
width: $bookmark_dropdown_item_icon_size;
|
||||||
|
height: $bookmark_dropdown_item_icon_size;
|
||||||
|
padding: $bookmark_dropdown_item_icon_padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: $bookmark_dropdown_item_background_selected;
|
||||||
|
&:hover {
|
||||||
|
background-color: $bookmark_dropdown_item_background_selected_hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
content: url('data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20x%3D%221.5%22%20y%3D%221.5%22%20width%3D%2221%22%20height%3D%2221%22%20rx%3D%223%22%20stroke%3D%22currentColor%22%20stroke-width%3D%223%22%20fill%3D%22none%22%2F%3E%3Crect%20x%3D%225%22%20y%3D%225%22%20width%3D%2214%22%20height%3D%2214%22%20rx%3D%222%22%20stroke%3D%22none%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks-dropdown-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bookmark_dropdown_items_container_max_height: 300px !default;
|
||||||
|
.bookmark-dropdown-items-container {
|
||||||
|
max-height: $bookmark_dropdown_items_container_max_height;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user