Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/Config/setting-defaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@
'app-editor' => 'wysiwyg',
'app-color' => '#206ea7',
'app-color-light' => 'rgba(32,110,167,0.15)',
'link-color' => '#206ea7',
'bookshelf-color' => '#a94747',
'book-color' => '#077b70',
'chapter-color' => '#af4d0d',
'page-color' => '#206ea7',
'page-draft-color' => '#7e50b1',
'app-color-dark' => '#195785',
'app-color-light-dark' => 'rgba(32,110,167,0.15)',
'link-color-dark' => '#429fe3',
'bookshelf-color-dark' => '#ff5454',
'book-color-dark' => '#389f60',
'chapter-color-dark' => '#ee7a2d',
'page-color-dark' => '#429fe3',
'page-draft-color-dark' => '#a66ce8',
'app-custom-head' => false,
'registration-enabled' => false,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CopyColorSettingsForDarkMode extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$colorSettings = [
'app-color',
'app-color-light',
'bookshelf-color',
'book-color',
'chapter-color',
'page-color',
'page-draft-color',
];

$existing = DB::table('settings')
->whereIn('setting_key', $colorSettings)
->get()->toArray();

$newData = [];
foreach ($existing as $setting) {
$newSetting = (array) $setting;
$newSetting['setting_key'] .= '-dark';
$newData[] = $newSetting;

if ($newSetting['setting_key'] === 'app-color-dark') {
$newSetting['setting_key'] = 'link-color';
$newData[] = $newSetting;
$newSetting['setting_key'] = 'link-color-dark';
$newData[] = $newSetting;
}
}

DB::table('settings')->insert($newData);
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$colorSettings = [
'app-color-dark',
'link-color',
'link-color-dark',
'app-color-light-dark',
'bookshelf-color-dark',
'book-color-dark',
'chapter-color-dark',
'page-color-dark',
'page-draft-color-dark',
];

DB::table('settings')
->whereIn('setting_key', $colorSettings)
->delete();
}
}
2 changes: 1 addition & 1 deletion resources/js/components/attachments.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class Attachments extends Component {
this.stopEdit();
/** @var {Tabs} */
const tabs = window.$components.firstOnElement(this.mainTabs, 'tabs');
tabs.show('items');
tabs.show('attachment-panel-items');
window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
this.list.innerHTML = resp.data;
window.$components.init(this.list);
Expand Down
7 changes: 3 additions & 4 deletions resources/js/components/image-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,9 @@ export class ImageManager extends Component {
}

setActiveFilterTab(filterName) {
this.filterTabs.forEach(t => t.classList.remove('selected'));
const activeTab = this.filterTabs.find(t => t.dataset.filter === filterName);
if (activeTab) {
activeTab.classList.add('selected');
for (const tab of this.filterTabs) {
const selected = tab.dataset.filter === filterName;
tab.setAttribute('aria-selected', selected ? 'true' : 'false');
}
}

Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export {PagePicker} from "./page-picker.js"
export {PermissionsTable} from "./permissions-table.js"
export {Pointer} from "./pointer.js"
export {Popup} from "./popup.js"
export {SettingAppColorPicker} from "./setting-app-color-picker.js"
export {SettingAppColorScheme} from "./setting-app-color-scheme.js"
export {SettingColorPicker} from "./setting-color-picker.js"
export {SettingHomepageControl} from "./setting-homepage-control.js"
export {ShelfSort} from "./shelf-sort.js"
Expand Down
49 changes: 0 additions & 49 deletions resources/js/components/setting-app-color-picker.js

This file was deleted.

82 changes: 82 additions & 0 deletions resources/js/components/setting-app-color-scheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {Component} from "./component";

export class SettingAppColorScheme extends Component {

setup() {
this.container = this.$el;
this.mode = this.$opts.mode;
this.lightContainer = this.$refs.lightContainer;
this.darkContainer = this.$refs.darkContainer;

this.container.addEventListener('tabs-change', event => {
const panel = event.detail.showing;
const newMode = (panel === 'color-scheme-panel-light') ? 'light' : 'dark';
this.handleModeChange(newMode);
});

const onInputChange = (event) => {
this.updateAppColorsFromInputs();

if (event.target.name.startsWith('setting-app-color')) {
this.updateLightForInput(event.target);
}
};
this.container.addEventListener('change', onInputChange);
this.container.addEventListener('input', onInputChange);
}

handleModeChange(newMode) {
this.mode = newMode;
const isDark = (newMode === 'dark');

document.documentElement.classList.toggle('dark-mode', isDark);
this.updateAppColorsFromInputs();
}

updateAppColorsFromInputs() {
const inputContainer = this.mode === 'dark' ? this.darkContainer : this.lightContainer;
const inputs = inputContainer.querySelectorAll('input[type="color"]');
for (const input of inputs) {
const splitName = input.name.split('-');
const colorPos = splitName.indexOf('color');
let cssId = splitName.slice(1, colorPos).join('-');
if (cssId === 'app') {
cssId = 'primary';
}

const varName = '--color-' + cssId;
document.body.style.setProperty(varName, input.value);
}
}

/**
* Update the 'light' app color variant for the given input.
* @param {HTMLInputElement} input
*/
updateLightForInput(input) {
const lightName = input.name.replace('-color', '-color-light');
const hexVal = input.value;
const rgb = this.hexToRgb(hexVal);
const rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';

console.log(input.name, lightName, hexVal, rgbLightVal)
const lightColorInput = this.container.querySelector(`input[name="${lightName}"][type="hidden"]`);
lightColorInput.value = rgbLightVal;
}

/**
* Covert a hex color code to rgb components.
* @attribution https://stackoverflow.com/a/5624139
* @param {String} hex
* @returns {{r: Number, g: Number, b: Number}}
*/
hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return {
r: result ? parseInt(result[1], 16) : 0,
g: result ? parseInt(result[2], 16) : 0,
b: result ? parseInt(result[3], 16) : 0
};
}

}
2 changes: 1 addition & 1 deletion resources/js/components/setting-color-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export class SettingColorPicker extends Component {

setValue(value) {
this.colorInput.value = value;
this.colorInput.dispatchEvent(new Event('change'));
this.colorInput.dispatchEvent(new Event('change', {bubbles: true}));
}
}
64 changes: 32 additions & 32 deletions resources/js/components/tabs.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import {onSelect} from "../services/dom";
import {Component} from "./component";

/**
* Tabs
* Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
* Uses accessible attributes to drive its functionality.
* On tab wrapping element:
* - role=tablist
* On tabs (Should be a button):
* - id
* - role=tab
* - aria-selected=true/false
* - aria-controls=<id-of-panel-section>
* On panels:
* - id
* - tabindex=0
* - role=tabpanel
* - aria-labelledby=<id-of-tab-for-panel>
* - hidden (If not shown by default).
*/
export class Tabs extends Component {

setup() {
this.tabContentsByName = {};
this.tabButtonsByName = {};
this.allContents = [];
this.allButtons = [];
this.container = this.$el;
this.tabs = Array.from(this.container.querySelectorAll('[role="tab"]'));
this.panels = Array.from(this.container.querySelectorAll('[role="tabpanel"]'));

for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
if (key.startsWith('toggle')) {
const cleanKey = key.replace('toggle', '').toLowerCase();
onSelect(elems, e => this.show(cleanKey));
this.allButtons.push(...elems);
this.tabButtonsByName[cleanKey] = elems;
this.container.addEventListener('click', event => {
const button = event.target.closest('[role="tab"]');
if (button) {
this.show(button.getAttribute('aria-controls'));
}
if (key.startsWith('content')) {
const cleanKey = key.replace('content', '').toLowerCase();
this.tabContentsByName[cleanKey] = elems;
this.allContents.push(...elems);
}
}
});
}

show(key) {
this.allContents.forEach(c => {
c.classList.add('hidden');
c.classList.remove('selected');
});
this.allButtons.forEach(b => b.classList.remove('selected'));
show(sectionId) {
for (const panel of this.panels) {
panel.toggleAttribute('hidden', panel.id !== sectionId);
}

const contents = this.tabContentsByName[key] || [];
const buttons = this.tabButtonsByName[key] || [];
if (contents.length > 0) {
contents.forEach(c => {
c.classList.remove('hidden')
c.classList.add('selected')
});
buttons.forEach(b => b.classList.add('selected'));
for (const tab of this.tabs) {
const tabSection = tab.getAttribute('aria-controls');
const selected = tabSection === sectionId;
tab.setAttribute('aria-selected', selected ? 'true' : 'false');
}

this.$emit('change', {showing: sectionId});
}

}
2 changes: 1 addition & 1 deletion resources/js/services/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function scrollAndHighlightElement(element) {
if (!element) return;
element.scrollIntoView({behavior: 'smooth'});

const color = document.getElementById('custom-styles').getAttribute('data-color-light');
const color = getComputedStyle(document.body).getPropertyValue('--color-primary-light');
const initColor = window.getComputedStyle(element).getPropertyValue('background-color');
element.style.backgroundColor = color;
setTimeout(() => {
Expand Down
10 changes: 6 additions & 4 deletions resources/lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
'app_icon' => 'Application Icon',
'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
'app_primary_color' => 'Application Primary Color',
'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
'app_homepage' => 'Application Homepage',
'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
'app_homepage_select' => 'Select a page',
Expand All @@ -51,8 +49,12 @@
'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',

// Color settings
'content_colors' => 'Content Colors',
'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
'color_scheme' => 'Application Color Scheme',
'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
'app_color' => 'Primary Color',
'link_color' => 'Default Link Color',
'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
'bookshelf_color' => 'Shelf Color',
'book_color' => 'Book Color',
'chapter_color' => 'Chapter Color',
Expand Down
7 changes: 7 additions & 0 deletions resources/sass/_blocks.scss
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,13 @@
}
}

.sub-card {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
border: 1.5px solid;
@include lightDark(border-color, #E2E2E2, #444);
border-radius: 4px;
}

.outline-hover {
border: 1px solid transparent !important;
&:hover {
Expand Down
Loading