mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-09-27 17:42:58 +00:00
Merge branch 'master' into issue_4975
This commit is contained in:
@@ -103,6 +103,7 @@ const CodeEditor = forwardRef(
|
||||
onChange = ()=>{},
|
||||
onCursorChange = ()=>{},
|
||||
onViewChange = ()=>{},
|
||||
onThemeChange = ()=>{},
|
||||
editorTheme = 'default',
|
||||
style,
|
||||
renderer,
|
||||
@@ -327,6 +328,9 @@ const CodeEditor = forwardRef(
|
||||
view.dispatch({
|
||||
effects : themeCompartment.reconfigure(themeExtension),
|
||||
});
|
||||
|
||||
const isDark = view.state.facet(EditorView.darkTheme);
|
||||
onThemeChange(isDark);
|
||||
}, [editorTheme, tab]);
|
||||
|
||||
useEffect(()=>{
|
||||
|
||||
@@ -7,6 +7,7 @@ const Combobox = createReactClass({
|
||||
displayName : 'Combobox',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
id : '',
|
||||
className : '',
|
||||
trigger : 'hover',
|
||||
default : '',
|
||||
@@ -75,6 +76,7 @@ const Combobox = createReactClass({
|
||||
onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined}
|
||||
{...(this.props.tooltip ? { 'data-tooltip-right': this.props.tooltip } : {})}>
|
||||
<input
|
||||
id={this.props.id}
|
||||
type='text'
|
||||
onChange={(e)=>this.handleInput(e)}
|
||||
value={this.state.value || ''}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
.item i {
|
||||
position : absolute;
|
||||
right : 10px;
|
||||
color : black;
|
||||
color : inherit;
|
||||
}
|
||||
.dropdown-options {
|
||||
position : absolute;
|
||||
@@ -32,14 +32,13 @@
|
||||
font-size : 11px;
|
||||
cursor : default;
|
||||
&:hover {
|
||||
background-color : rgb(163, 163, 163);
|
||||
filter : brightness(120%);
|
||||
background-color : #ddd;
|
||||
}
|
||||
.detail {
|
||||
width : 100%;
|
||||
font-size : 9px;
|
||||
font-style : italic;
|
||||
color : rgb(124, 124, 124);
|
||||
color : #7c7c7c;
|
||||
text-align : left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@property --activeTriggerColor {
|
||||
syntax: '<color>';
|
||||
inherits: true;
|
||||
initial-value: #DDD;
|
||||
initial-value: #999;
|
||||
}
|
||||
|
||||
:root{
|
||||
@@ -31,3 +31,7 @@
|
||||
.menu-wrapper:has(:popover-open) > button { // if menu is open...
|
||||
background-color: var(--activeTriggerColor, hsl(from var(--menuColor) h s calc(l * .85))); // tint menu triggers based on menu color
|
||||
}
|
||||
|
||||
.darkMode .menu-wrapper:has(:popover-open) > button { // if menu is open...
|
||||
--activeTriggerColor : #444; // tint menu triggers based on menu color
|
||||
}
|
||||
@@ -41,7 +41,6 @@ const BrewPage = (props)=>{
|
||||
props = {
|
||||
contents : '',
|
||||
index : 0,
|
||||
hoisted : false,
|
||||
...props
|
||||
};
|
||||
const pageRef = useRef(null);
|
||||
@@ -220,7 +219,7 @@ const BrewRenderer = (props)=>{
|
||||
}
|
||||
};
|
||||
|
||||
const renderPages = (checkHoists = false)=>{
|
||||
const renderPages = ()=>{
|
||||
if(props.errors?.length)
|
||||
return renderedPages;
|
||||
|
||||
@@ -234,16 +233,10 @@ const BrewRenderer = (props)=>{
|
||||
renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
|
||||
|
||||
_.forEach(rawPages, (page, index)=>{
|
||||
const varsOnPageRegex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g; // Find out if there are any vars on the page.
|
||||
const forceRender = checkHoists &&
|
||||
!props.hoisted &&
|
||||
(page.match(varsOnPageRegex)); // forceRender forces pages outside of the PPR range to render if true.
|
||||
// This is necessary on the first load to fully populate the variable table.
|
||||
if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){
|
||||
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
||||
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
|
||||
}
|
||||
});
|
||||
props.hoisted = true; // Only fully hoist once.
|
||||
return renderedPages;
|
||||
};
|
||||
|
||||
@@ -283,7 +276,7 @@ const BrewRenderer = (props)=>{
|
||||
window.addEventListener('hashchange', ()=>scrollToHash(window.location.hash));
|
||||
|
||||
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
|
||||
renderPages(true); //Make sure page is renderable before showing
|
||||
renderPages(); //Make sure page is renderable before showing
|
||||
setState((prevState)=>({
|
||||
...prevState,
|
||||
isMounted : true,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import './editor.less';
|
||||
import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import dedent from 'dedent';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
|
||||
import CodeEditor from '@components/codeEditor/codeEditor.jsx';
|
||||
import SnippetBar from './snippetbar/snippetbar.jsx';
|
||||
@@ -86,6 +87,7 @@ const Editor = forwardRef(
|
||||
)=>{
|
||||
const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet'
|
||||
const [snippetBarHeight, setSnippetBarHeight] = useState(26);
|
||||
const [isDark, setIsDark] = useState(false);
|
||||
const [editorSettings, setEditorSettings] = useState({
|
||||
autoCloseBrackets : true,
|
||||
showImagePreviews : true,
|
||||
@@ -261,6 +263,7 @@ const Editor = forwardRef(
|
||||
onCursorChange={(page)=>updateCurrentCursorPage(page)}
|
||||
onViewChange={(page)=>updateCurrentViewPage(page)}
|
||||
editorTheme={editorSettings.editorTheme}
|
||||
onThemeChange={setIsDark}
|
||||
renderer={brew.renderer}
|
||||
style={{ height: `calc(100% - ${snippetBarHeight}px)` }}
|
||||
settings={editorSettings}
|
||||
@@ -280,6 +283,7 @@ const Editor = forwardRef(
|
||||
value={brew.style ?? DEFAULT_STYLE_TEXT}
|
||||
onChange={onBrewChange('style')}
|
||||
editorTheme={editorSettings.editorTheme}
|
||||
onThemeChange={setIsDark}
|
||||
renderer={brew.renderer}
|
||||
style={{ height: `calc(100% - ${snippetBarHeight}px)` }}
|
||||
settings={editorSettings}
|
||||
@@ -303,6 +307,7 @@ const Editor = forwardRef(
|
||||
onChange={onBrewChange('snippets')}
|
||||
enableFolding={true}
|
||||
editorTheme={editorSettings.editorTheme}
|
||||
onThemeChange={setIsDark}
|
||||
renderer={brew.renderer}
|
||||
style={{ height: `calc(100% - 25px)` }}
|
||||
settings={editorSettings}
|
||||
@@ -313,7 +318,7 @@ const Editor = forwardRef(
|
||||
if(isMeta()) {
|
||||
return (
|
||||
<>
|
||||
<CodeEditor key='codeEditor' tab='brewMetadata' view={view} style={{ display: 'none' }} settings={editorSettings} />
|
||||
<CodeEditor key='codeEditor' tab='brewMetadata' editorTheme={editorSettings.editorTheme} onThemeChange={setIsDark} view={view} style={{ display: 'none' }} settings={editorSettings} />
|
||||
<MetadataEditor
|
||||
metadata={brew}
|
||||
themeBundle={themeBundle}
|
||||
@@ -332,6 +337,8 @@ const Editor = forwardRef(
|
||||
tab='brewSettings' //necessary or the brew object loses its contents, culprit possibly on the tab dependent useEffect in codeEditor.jsx
|
||||
view={view}
|
||||
style={{ display: 'none' }}
|
||||
editorTheme={editorSettings.editorTheme}
|
||||
onThemeChange={setIsDark}
|
||||
settings={editorSettings}
|
||||
/>
|
||||
<SettingsEditor
|
||||
@@ -362,7 +369,7 @@ const Editor = forwardRef(
|
||||
historySize,
|
||||
}));
|
||||
return (
|
||||
<div className='editor' ref={editor}>
|
||||
<div className={`editor${isDark ? ' darkMode' : ''}`} ref={editor}>
|
||||
<SnippetBar
|
||||
brew={brew}
|
||||
view={view}
|
||||
|
||||
@@ -7,7 +7,6 @@ import request from '../../utils/request-middleware.js';
|
||||
import Combobox from '@components/combobox.jsx';
|
||||
import TagInput from '../tagInput/tagInput.jsx';
|
||||
|
||||
|
||||
import Themes from '@themes/themes.json';
|
||||
import validations from './validations.js';
|
||||
|
||||
@@ -84,7 +83,6 @@ const MetadataEditor = createReactClass({
|
||||
return `- ${err}`;
|
||||
}).join('\n');
|
||||
|
||||
|
||||
debouncedReportValidity(e.target, errMessage);
|
||||
return false;
|
||||
}
|
||||
@@ -156,11 +154,11 @@ const MetadataEditor = createReactClass({
|
||||
|
||||
renderPublish : function(){
|
||||
if(this.props.metadata.published){
|
||||
return <button className='unpublish' onClick={()=>this.handlePublish(false)}>
|
||||
return <button id='publish-button' className='unpublish' onClick={()=>this.handlePublish(false)}>
|
||||
<i className='fas fa-ban' aria-hidden='true' /> unpublish
|
||||
</button>;
|
||||
} else {
|
||||
return <button className='publish' onClick={()=>this.handlePublish(true)}>
|
||||
return <button id='publish-button' className='publish' onClick={()=>this.handlePublish(true)}>
|
||||
<i className='fas fa-globe' aria-hidden='true' /> publish
|
||||
</button>;
|
||||
}
|
||||
@@ -170,9 +168,9 @@ const MetadataEditor = createReactClass({
|
||||
if(!this.props.metadata.editId) return;
|
||||
|
||||
return <div className='field delete'>
|
||||
<label>delete</label>
|
||||
<label htmlFor='delete-button'>delete</label>
|
||||
<div className='value'>
|
||||
<button className='publish' onClick={this.handleDelete}>
|
||||
<button id='delete-button' onClick={this.handleDelete}>
|
||||
<i className='fas fa-trash-alt' /> delete brew
|
||||
</button>
|
||||
</div>
|
||||
@@ -186,7 +184,7 @@ const MetadataEditor = createReactClass({
|
||||
<label>authors</label>
|
||||
<div className='value'>
|
||||
{authors.length > 0 && (
|
||||
<a href={`/user/${authors[0]}`} className='author-link' target="_blank" title={`Owner - Click to open ${authors[0]}'s profile in a new tab`}>
|
||||
<a href={`/user/${authors[0]}`} className='author-link' target='_blank' title={`Owner - Click to open ${authors[0]}'s profile in a new tab`}>
|
||||
{authors[0]}{authors.length > 1 && ', '}
|
||||
</a>
|
||||
)}
|
||||
@@ -227,7 +225,6 @@ const MetadataEditor = createReactClass({
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
},
|
||||
|
||||
renderThemeDropdown : function(){
|
||||
@@ -264,6 +261,7 @@ const MetadataEditor = createReactClass({
|
||||
dropdown =
|
||||
<div className='value' data-tooltip-top='Select from the list below (built-in themes and brews you have tagged "meta:theme"), or paste in the Share URL or Share ID of any brew.'>
|
||||
<Combobox trigger='click'
|
||||
id='combobox-themes'
|
||||
className='themes-dropdown'
|
||||
default={currentThemeDisplay}
|
||||
placeholder='Select from below, or enter the Share URL or ID of a brew with the meta:theme tag'
|
||||
@@ -284,7 +282,7 @@ const MetadataEditor = createReactClass({
|
||||
}
|
||||
|
||||
return <div className='field themes'>
|
||||
<label>theme</label>
|
||||
<label htmlFor='combobox-themes'>theme</label>
|
||||
{dropdown}
|
||||
</div>;
|
||||
},
|
||||
@@ -303,9 +301,10 @@ const MetadataEditor = createReactClass({
|
||||
};
|
||||
|
||||
return <div className='field language'>
|
||||
<label>language</label>
|
||||
<label htmlFor='combobox-language'>language</label>
|
||||
<div className='value' data-tooltip-right='Sets the HTML Lang property for your brew. May affect hyphenation or spellcheck.'>
|
||||
<Combobox trigger='click'
|
||||
id='combobox-language'
|
||||
className='language-dropdown'
|
||||
default={this.props.metadata.lang || ''}
|
||||
placeholder='en'
|
||||
@@ -355,24 +354,24 @@ const MetadataEditor = createReactClass({
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='metadataEditor ui-editor'>
|
||||
return <div className='metadataEditor uiEditor'>
|
||||
<h1>Properties Editor</h1>
|
||||
|
||||
<div className='field title'>
|
||||
<label for='title_field'>title</label>
|
||||
<label htmlFor='title_field'>title</label>
|
||||
<input type='text' id='title_field' className='value'
|
||||
defaultValue={this.props.metadata.title}
|
||||
onChange={(e)=>this.handleFieldChange('title', e)} />
|
||||
</div>
|
||||
<div className='field-group'>
|
||||
<fieldset className='field-group'>
|
||||
<div className='field-column'>
|
||||
<div className='field description'>
|
||||
<label for='description_field'>description</label>
|
||||
<label htmlFor='description_field'>description</label>
|
||||
<textarea id='description_field' defaultValue={this.props.metadata.description} className='value'
|
||||
onChange={(e)=>this.handleFieldChange('description', e)} />
|
||||
</div>
|
||||
<div className='field thumbnail'>
|
||||
<label for='thumbnail_field'>thumbnail</label>
|
||||
<label htmlFor='thumbnail_field'>thumbnail</label>
|
||||
<input type='text'
|
||||
id='thumbnail_field'
|
||||
defaultValue={this.props.metadata.thumbnail}
|
||||
@@ -386,12 +385,13 @@ const MetadataEditor = createReactClass({
|
||||
</div>
|
||||
</div>
|
||||
{this.renderThumbnail()}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div className='field tags'>
|
||||
<label>Tags</label>
|
||||
<label htmlFor='combobox-tags'>Tags</label>
|
||||
<div className='value' >
|
||||
<TagInput
|
||||
id='combobox-tags'
|
||||
label='tags'
|
||||
valuePatterns={/^\s*(?:(?:group|meta|system|type)\s*:\s*)?[A-Za-z0-9][A-Za-z0-9 \/\\.&_\-]{0,40}\s*$/}
|
||||
placeholder='add tag' unique={true}
|
||||
@@ -402,7 +402,6 @@ const MetadataEditor = createReactClass({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{this.renderLanguageDropdown()}
|
||||
|
||||
{this.renderThemeDropdown()}
|
||||
@@ -414,9 +413,10 @@ const MetadataEditor = createReactClass({
|
||||
{this.renderAuthors()}
|
||||
|
||||
<div className='field invitedAuthors'>
|
||||
<label>Invited authors</label>
|
||||
<label htmlFor='combobox-invited-authors'>Invited authors</label>
|
||||
<div className='value'>
|
||||
<TagInput
|
||||
id='combobox-invited-authors'
|
||||
label='invited authors'
|
||||
valuePatterns={/.+/}
|
||||
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
||||
@@ -429,11 +429,10 @@ const MetadataEditor = createReactClass({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Privacy</h2>
|
||||
|
||||
<div className='field publish'>
|
||||
<label>publish</label>
|
||||
<label htmlFor='publish-button'>publish</label>
|
||||
<div className='value'>
|
||||
{this.renderPublish()}
|
||||
<small>Published brews are searchable in <a href='/vault'>the Vault</a> and visible on your user page. Unpublished brews are not indexed in the Vault or visible on your user page, but can still be shared and indexed by search engines. You can unpublish a brew any time.</small>
|
||||
|
||||
@@ -3,9 +3,7 @@ import React from 'react';
|
||||
|
||||
const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList })=>{
|
||||
|
||||
const validations = {
|
||||
|
||||
};
|
||||
const validations = {};
|
||||
|
||||
const handleFieldChange = (setting, e)=>{
|
||||
const value =
|
||||
@@ -38,7 +36,7 @@ const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='settingsEditor ui-editor'>
|
||||
<div className='settingsEditor uiEditor'>
|
||||
<h1>Editor Settings</h1>
|
||||
|
||||
<div className='field'>
|
||||
@@ -119,17 +117,23 @@ const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList
|
||||
</label>
|
||||
|
||||
<div className='value'>
|
||||
<small style={{ fontSize: `${settings.fontSize || 1}em` }}>from 9px to 30px</small>
|
||||
<small style={{ fontSize: `${settings.fontSize || 1}em` }}>from 7px to 26px</small>
|
||||
<input
|
||||
id='fontSize'
|
||||
type='range'
|
||||
min={.6}
|
||||
min={.5}
|
||||
step={.1}
|
||||
max={2}
|
||||
list='font-sizes'
|
||||
name='fontSize'
|
||||
value={settings.fontSize}
|
||||
title={`${Math.round(settings.fontSize * 13)}px`}
|
||||
value={settings.fontSize || 1}
|
||||
onChange={(e)=>handleFieldChange('fontSize', e)}
|
||||
/>
|
||||
<datalist id='font-sizes'>
|
||||
<option value='1' />
|
||||
</datalist>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,21 +3,23 @@
|
||||
@import (less) '@themes/fonts/5e/fonts.less';
|
||||
|
||||
.snippetBar {
|
||||
--activeTriggerColor : inherit;
|
||||
--menuColor : #DDDDDD;
|
||||
--textColor : black;
|
||||
--hoverMenuColor : #999;
|
||||
|
||||
@menuHeight : 25px;
|
||||
position : relative;
|
||||
display : flex;
|
||||
flex-wrap : wrap-reverse;
|
||||
reading-flow : flex-visual;
|
||||
justify-content : space-between;
|
||||
height : auto;
|
||||
color : var(--textColor);
|
||||
background-color : var(--menuColor);
|
||||
font-size : .65rem;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size : 0.65rem;
|
||||
font-weight : 800;
|
||||
color : black;
|
||||
text-transform: uppercase;
|
||||
background-color : #DDDDDD;
|
||||
reading-flow : flex-visual;
|
||||
font-weight: 800;
|
||||
|
||||
.editors {
|
||||
display : flex;
|
||||
@@ -44,7 +46,7 @@
|
||||
|
||||
&.editorTool:not(.active) { cursor : not-allowed; }
|
||||
|
||||
&:hover,&.selected { background-color : #999999; }
|
||||
&:hover,&.selected { background-color : var(--hoverMenuColor) }
|
||||
&.text {
|
||||
.tooltipLeft('Brew Editor');
|
||||
}
|
||||
@@ -101,11 +103,6 @@
|
||||
background-color : #999999;
|
||||
}
|
||||
}
|
||||
&.divider {
|
||||
width : 5px;
|
||||
background : linear-gradient(currentColor, currentColor) no-repeat center/1px 100%;
|
||||
&:hover { background-color : inherit; }
|
||||
}
|
||||
}
|
||||
.themeSelector {
|
||||
position : absolute;
|
||||
@@ -202,7 +199,9 @@
|
||||
background : grey;
|
||||
border-radius : 12px;
|
||||
}
|
||||
&:hover { background-color : #999999; }
|
||||
&:hover {
|
||||
background-color : var(--hoverMenuColor);
|
||||
}
|
||||
&:disabled {
|
||||
color : gray;
|
||||
cursor : not-allowed;
|
||||
@@ -226,3 +225,9 @@
|
||||
.editors > div.history > .dropdown { right : unset; }
|
||||
}
|
||||
}
|
||||
|
||||
.editor.darkMode .snippetBar {
|
||||
--menuColor : #666;
|
||||
--textColor : #eee;
|
||||
--hoverMenuColor : #444;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Combobox from '@components/combobox.jsx';
|
||||
|
||||
import { tagSuggestionList, canonizationList } from './curatedTagSuggestionList.js';
|
||||
|
||||
const TagInput = ({ tooltip, label, valuePatterns, values = [], unique = true, placeholder = '', smallText = '', onChange })=>{
|
||||
const TagInput = ({ id, tooltip, label, valuePatterns, values = [], unique = true, placeholder = '', smallText = '', onChange })=>{
|
||||
const [tagList, setTagList] = useState(
|
||||
values.map((value)=>({
|
||||
value,
|
||||
@@ -128,6 +128,7 @@ const TagInput = ({ tooltip, label, valuePatterns, values = [], unique = true, p
|
||||
return (
|
||||
<div className='tagInputWrap'>
|
||||
<Combobox
|
||||
id={id}
|
||||
trigger='click'
|
||||
className='tagInput-dropdown'
|
||||
default=''
|
||||
@@ -155,6 +156,7 @@ const TagInput = ({ tooltip, label, valuePatterns, values = [], unique = true, p
|
||||
<ul className='list'>
|
||||
{tagList.map((t, i)=>t.editing ? (
|
||||
<input
|
||||
id={`${id}-${i}`}
|
||||
key={i}
|
||||
type='text'
|
||||
value={t.draft} // always use draft
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '@themes/assets/assets.less';
|
||||
@import '@sharedStyles/core.less';
|
||||
|
||||
.userThemeName {
|
||||
@@ -5,7 +6,17 @@
|
||||
padding-left : 10px;
|
||||
}
|
||||
|
||||
.ui-editor {
|
||||
.uiEditor {
|
||||
--bg-clr : white;
|
||||
--bg-image : @backgroundImageAlt;
|
||||
--h1-clr : black;
|
||||
--h2-clr : #000077;
|
||||
--label-clr : black;
|
||||
--input-bg : white;
|
||||
--input-bg-hover : #DDDDDD;
|
||||
--input-text-clr : black;
|
||||
--input-placeholder-clr : grey;
|
||||
|
||||
position : absolute;
|
||||
box-sizing : border-box;
|
||||
width : 100%;
|
||||
@@ -13,23 +24,28 @@
|
||||
padding : 25px;
|
||||
overflow-y : auto;
|
||||
font-size : 13px;
|
||||
background-color : #999999;
|
||||
color : var(--label-clr);
|
||||
background-color : var(--bg-clr);
|
||||
background-image : var(--bg-image);
|
||||
background-size : cover;
|
||||
|
||||
h1 {
|
||||
margin : 0 0 40px;
|
||||
font-size : 15px;
|
||||
font-size : 18px;
|
||||
font-weight : bold;
|
||||
color : var(--h1-clr);
|
||||
text-transform : uppercase;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin : 20px 0;
|
||||
margin : 16px 0;
|
||||
font-size : 15px;
|
||||
font-weight : bold;
|
||||
color : #555555;
|
||||
border-bottom : 2px solid gray;
|
||||
color : var(--h2-clr);
|
||||
border-bottom : 2px solid currentColor;
|
||||
}
|
||||
|
||||
& > div { margin-bottom : 10px; }
|
||||
& > div ,& > fieldset { margin-bottom : 10px; }
|
||||
|
||||
.field-group {
|
||||
display : flex;
|
||||
@@ -40,7 +56,7 @@
|
||||
|
||||
.field-column {
|
||||
display : flex;
|
||||
flex : 5 0 200px;
|
||||
flex : 5 0 250px;
|
||||
flex-direction : column;
|
||||
gap : 10px;
|
||||
}
|
||||
@@ -50,13 +66,16 @@
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
width : 100%;
|
||||
min-width : 200px;
|
||||
min-width : 250px;
|
||||
padding-left : 10px;
|
||||
|
||||
& > label {
|
||||
width : 80px;
|
||||
font-size : 0.9em;
|
||||
width : 100px;
|
||||
font-size : 1em;
|
||||
font-weight : 800;
|
||||
line-height : 1.8em;
|
||||
text-transform : uppercase;
|
||||
color : inherit;
|
||||
text-transform : capitalize;
|
||||
}
|
||||
& > .value {
|
||||
flex : 1 1 auto;
|
||||
@@ -72,14 +91,22 @@
|
||||
line-height : 1.4em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
input[type='checkbox'], input[type='number'] {
|
||||
float : right;
|
||||
text-align : end;
|
||||
}
|
||||
input[type='text'], textarea, select {
|
||||
color : var(--input-text-clr);
|
||||
background-color : var(--input-bg);
|
||||
border : 1px solid var(--input-placeholder-clr);
|
||||
&:hover, :focus { outline : 1px solid var(--input-placeholder-clr); background-color : var(--input-bg-hover); }
|
||||
&::placeholder { color : var(--input-placeholder-clr); }
|
||||
}
|
||||
input[type='text'], textarea {
|
||||
border : 1px solid gray;
|
||||
&:focus { outline : 1px solid #444444; }
|
||||
|
||||
input[type='range'] {
|
||||
color: var(--input-text-clr);
|
||||
}
|
||||
|
||||
&.description {
|
||||
@@ -90,7 +117,6 @@
|
||||
resize : none;
|
||||
}
|
||||
}
|
||||
|
||||
&.thumbnail, &.themes {
|
||||
label { line-height : 2.0em; }
|
||||
.value {
|
||||
@@ -100,53 +126,100 @@
|
||||
button {
|
||||
.colorButton();
|
||||
padding : 0px 5px;
|
||||
color : white;
|
||||
background-color : black;
|
||||
color : var(--input-text-clr);
|
||||
background-color : var(--input-bg);
|
||||
border : 1px solid #999999;
|
||||
&:hover { background-color : #777777; }
|
||||
}
|
||||
}
|
||||
|
||||
&.tags .tagInput-dropdown {
|
||||
z-index : 400;
|
||||
max-width : 200px;
|
||||
|
||||
.dropdown-options {
|
||||
color : var(--input-text-clr);
|
||||
background-color : var(--input-bg);
|
||||
|
||||
.item:hover {
|
||||
color : var(--input-bg);
|
||||
background-color : var(--input-text-clr);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.language .value {
|
||||
z-index : 300;
|
||||
max-width : 150px;
|
||||
}
|
||||
|
||||
&.themes {
|
||||
.value {
|
||||
overflow : visible;
|
||||
text-overflow : auto;
|
||||
}
|
||||
button {
|
||||
padding-right : 5px;
|
||||
padding-left : 5px;
|
||||
}
|
||||
& .dropdown-container { z-index : 200; }
|
||||
& .dropdown-options { overflow-y : visible; }
|
||||
.disabled {
|
||||
font-style : italic;
|
||||
color : dimgray;
|
||||
background-color : darkgray;
|
||||
}
|
||||
|
||||
&.invitedAuthors .value {
|
||||
z-index : 100;
|
||||
|
||||
.tagInput-dropdown { max-width : 200px; }
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-preview {
|
||||
.item.dropdown-input { border:none;}
|
||||
.item {
|
||||
position : relative;
|
||||
flex : 1 1;
|
||||
justify-self : center;
|
||||
width : 80px;
|
||||
height : min-content;
|
||||
max-height : 115px;
|
||||
aspect-ratio : 1 / 1;
|
||||
object-fit : contain;
|
||||
background-color : #AAAAAA;
|
||||
overflow : visible;
|
||||
color : inherit;
|
||||
background-color : var(--input-bg);
|
||||
border-top : 1px solid #767676;
|
||||
.preview {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
right : 0;
|
||||
z-index : 10;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
width : 200px;
|
||||
overflow : hidden;
|
||||
color : var(--input-text-clr);
|
||||
background : var(--input-bg);
|
||||
border-radius : 5px;
|
||||
box-shadow : 0 0 5px var(--input-text-clr);
|
||||
opacity : 0;
|
||||
transition : opacity 250ms ease;
|
||||
h6 {
|
||||
padding-block : 0.5em;
|
||||
padding-inline : 1em;
|
||||
font-weight : 900;
|
||||
border-bottom : 2px solid #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.renderers.field .value {
|
||||
input { padding-right : 25px; }
|
||||
|
||||
.texture-container {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
left : 0;
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
min-height : 100%;
|
||||
overflow : hidden;
|
||||
> img {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
right : 0;
|
||||
width : 50%;
|
||||
min-height : 100%;
|
||||
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||
mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover > .preview { opacity : 1; }
|
||||
}
|
||||
}
|
||||
&.renderers .value {
|
||||
label {
|
||||
display : inline-flex;
|
||||
align-items : center;
|
||||
@@ -164,24 +237,8 @@
|
||||
cursor : pointer;
|
||||
}
|
||||
}
|
||||
.publish.field .value {
|
||||
position : relative;
|
||||
margin-bottom : 15px;
|
||||
button { width : 100%; }
|
||||
button.publish {
|
||||
.colorButton(@blueLight);
|
||||
}
|
||||
button.unpublish {
|
||||
.colorButton(@silver);
|
||||
}
|
||||
}
|
||||
|
||||
.delete.field .value {
|
||||
button {
|
||||
.colorButton(@red);
|
||||
}
|
||||
}
|
||||
.authors.field {
|
||||
&.authors {
|
||||
.tag {
|
||||
font-weight : 300;
|
||||
transition : background-color 0.2s;
|
||||
@@ -213,84 +270,33 @@
|
||||
|
||||
button { color : @red; }
|
||||
|
||||
}
|
||||
a { text-underline-offset : 0.2em; }
|
||||
}
|
||||
&.invitedAuthors .value {
|
||||
z-index : 100;
|
||||
|
||||
}
|
||||
a {
|
||||
color : black;
|
||||
text-underline-offset : 0.2em;
|
||||
}
|
||||
.tagInput-dropdown { max-width : 200px; }
|
||||
}
|
||||
|
||||
.themes.field {
|
||||
& .dropdown-container {
|
||||
&.publish .value {
|
||||
position : relative;
|
||||
z-index : 200;
|
||||
background-color : white;
|
||||
margin-bottom : 15px;
|
||||
button { width : 100%; }
|
||||
button.publish {
|
||||
.colorButton(@blueLight);
|
||||
}
|
||||
& .dropdown-options { overflow-y : visible; }
|
||||
.disabled {
|
||||
font-style : italic;
|
||||
color : dimgray;
|
||||
background-color : darkgray;
|
||||
button.unpublish {
|
||||
.colorButton(@silver);
|
||||
}
|
||||
.item {
|
||||
position : relative;
|
||||
padding : 3px 3px;
|
||||
overflow : visible;
|
||||
background-color : white;
|
||||
border-top : 1px solid rgb(118, 118, 118);
|
||||
.preview {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
right : 0;
|
||||
z-index : 1;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
width : 200px;
|
||||
overflow : hidden;
|
||||
color : black;
|
||||
background : #CCCCCC;
|
||||
border-radius : 5px;
|
||||
box-shadow : 0 0 5px black;
|
||||
opacity : 0;
|
||||
transition : opacity 250ms ease;
|
||||
h6 {
|
||||
padding-block : 0.5em;
|
||||
padding-inline : 1em;
|
||||
font-weight : 900;
|
||||
border-bottom : 2px solid hsl(0,0%,40%);
|
||||
}
|
||||
&.delete .value {
|
||||
button {
|
||||
.colorButton(@red);
|
||||
}
|
||||
}
|
||||
|
||||
.texture-container {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
left : 0;
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
min-height : 100%;
|
||||
overflow : hidden;
|
||||
> img {
|
||||
position : absolute;
|
||||
top : 0;
|
||||
right : 0;
|
||||
width : 50%;
|
||||
min-height : 100%;
|
||||
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||
mask-image : linear-gradient(90deg, transparent, black 20%);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color : white;
|
||||
background-color : @blue;
|
||||
filter : unset;
|
||||
}
|
||||
&:hover > .preview { opacity : 1; }
|
||||
}
|
||||
}
|
||||
|
||||
.field .list {
|
||||
.list {
|
||||
display : flex;
|
||||
flex : 1 0;
|
||||
flex-wrap : wrap;
|
||||
@@ -324,12 +330,15 @@
|
||||
padding : 0.35em;
|
||||
margin : 2px;
|
||||
font-size : 0.95em;
|
||||
background-color : #DDDDDD;
|
||||
border-radius : 0.5em;
|
||||
|
||||
.icon { #groupedIcon; }
|
||||
|
||||
button { cursor : pointer; }
|
||||
button {
|
||||
cursor : pointer;
|
||||
|
||||
&:hover { color : @redLight; }
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@@ -373,6 +382,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-preview {
|
||||
position : relative;
|
||||
flex : 1 1;
|
||||
justify-self : center;
|
||||
width : 80px;
|
||||
height : min-content;
|
||||
max-height : 115px;
|
||||
aspect-ratio : 1 / 1;
|
||||
object-fit : contain;
|
||||
background-color : #AAAAAA;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color : var(--input-bg);
|
||||
background : var(--input-text-clr);
|
||||
}
|
||||
}
|
||||
|
||||
.settingsEditor .field {
|
||||
padding-bottom : 10px;
|
||||
@@ -389,3 +415,28 @@
|
||||
max-width : calc(100% - 200px);
|
||||
}
|
||||
}
|
||||
|
||||
.editor.darkMode .uiEditor {
|
||||
--bg-clr : #555555;
|
||||
--bg-image : @backgroundImageAltDark;
|
||||
--h1-clr : white;
|
||||
--h2-clr : #AAAAFF;
|
||||
--label-clr : #DDDDDD;
|
||||
--input-bg : #333333;
|
||||
--input-bg-hover : #666666;
|
||||
--input-text-clr : #EEEEEE;
|
||||
--input-placeholder-clr : #AAAAAA;
|
||||
|
||||
.field { border-color : var(--h1-clr); }
|
||||
|
||||
a {
|
||||
color : @blueLight;
|
||||
|
||||
&:visited { color : #CB8AD8; }
|
||||
}
|
||||
|
||||
.thumbnail-preview[src='/client/homebrew/thumbnail.png'] { filter : invert(0.8); }
|
||||
button.publish {
|
||||
.colorButton(#3137de) !important;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -280,9 +280,14 @@ export default async function createApp(vite) {
|
||||
html = await vite.transformIndexHtml(req.originalUrl, html);
|
||||
}
|
||||
|
||||
const safeProps = JSON.stringify(props).replace(/<(?=\/?script)/ig, '\\u003c');
|
||||
html = html.replace(
|
||||
'<head>',
|
||||
()=>{ return `<head>\n<script id="props" >window.__INITIAL_PROPS__ = ${JSON.stringify(props)}</script>\n${ogMetaTags}`; }
|
||||
`<head>\n`
|
||||
+ `<script id="props">`
|
||||
+ `window.__INITIAL_PROPS__ = ` + safeProps
|
||||
+ `</script>\n`
|
||||
+ ogMetaTags
|
||||
);
|
||||
|
||||
return html;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@footerAccentImage : url('/assets/PHB_footerAccent.png');
|
||||
@frameBorderImage : url('/assets/frameBorder.png');
|
||||
@backgroundImage : url('/assets/parchmentBackground.jpg');
|
||||
@backgroundImageAlt : url('/assets/fluffy_background.webp');
|
||||
@backgroundImageAltDark : url('/assets/fluffy_background_dark.webp');
|
||||
@redTriangleImage : url('/assets/redTriangle.png');
|
||||
@monsterBorderImageLegacy : url('/assets/monsterBorderLegacy.png');
|
||||
@noteBorderImage : url('/assets/noteBorder.png');
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
+6
-6
@@ -8,12 +8,12 @@ export default defineConfig({
|
||||
plugins : [react(), generateAssetsPlugin()],
|
||||
resolve : {
|
||||
alias : {
|
||||
'@vitreum' : path.resolve(__dirname, './vitreum'),
|
||||
'@shared' : path.resolve(__dirname, './shared'),
|
||||
'@sharedStyles' : path.resolve(__dirname, './shared/naturalcrit/styles'),
|
||||
'@navbar' : path.resolve(__dirname, './client/homebrew/navbar'),
|
||||
'@themes' : path.resolve(__dirname, './themes'),
|
||||
'@components' : path.resolve(__dirname, './client/components')
|
||||
'@vitreum' : path.resolve(import.meta.dirname, './vitreum'),
|
||||
'@shared' : path.resolve(import.meta.dirname, './shared'),
|
||||
'@sharedStyles' : path.resolve(import.meta.dirname, './shared/naturalcrit/styles'),
|
||||
'@navbar' : path.resolve(import.meta.dirname, './client/homebrew/navbar'),
|
||||
'@themes' : path.resolve(import.meta.dirname, './themes'),
|
||||
'@components' : path.resolve(import.meta.dirname, './client/components')
|
||||
},
|
||||
},
|
||||
build : {
|
||||
|
||||
Reference in New Issue
Block a user